<< Back to previous view

[TCHECK-153] Metadata for tuning on generated values Created: 30/Jan/19  Updated: 09/Mar/19

Status: Open
Project: test.check
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: JAre Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: generative-test, generator, metadata, spec


 Description   

It can be really useful for optimization of generative testing(with specs) if values outputted by generators included attempts and time as metadata.
Example:

(-> (s/exercise string? 1)
first
meta)
;; => {:time-ms 2, :attemts 1}

It can help in finding slowest generators which can benefit the most from customization.



 Comments   
Comment by Alex Miller [ 30/Jan/19 7:27 AM ]

I move this to test.check as that’s where the generators live.

Note that not every value can carry metadata (number, strings, etc) but it’s an interesting idea.

Comment by JAre [ 30/Jan/19 10:25 AM ]

> Note that not every value can carry metadata (number, strings, etc) but it’s an interesting idea.

It's not that big of a deal since the metadata will be directly useful mostly with recursive/nested specs for spec tooling devs. And a person that wants to use it for this can work around the limitation by wrapping primitives.

For the end-user API something similar to spec/explain(s/explain-data, s/explain-str..) would be great.
I'll call it s/complexity, the function takes a spec and returns (in the case of s/complexity-data) structure of the same shape as the original spec filled with generation complexity data (average time, attempts, mb the spec form...) It should handle too complex specs in a reasonable way i.e. Instead of throwing (as with generators) it preferably reports which part of the spec is too complex and what is individual complexity of its elements.

Comment by JAre [ 30/Jan/19 10:37 AM ]

Also `s/complexity` probably should have per node(spec element) timeout (mb configurable) since it's mostly debugging/profiling tool.

Comment by Gary Fredericks [ 09/Feb/19 9:17 AM ]

What does "attempts" mean here? Does it only apply to gen/such-that generators?

Comment by JAre [ 09/Feb/19 10:00 AM ]

Yeah I in the context of specs. How many times generator had to retry before it managed to satisfy spec.

Comment by Gary Fredericks [ 24/Feb/19 6:45 PM ]

I just noticed the :time-ms aspect of this, which would presumably apply to a lot more than just such-that.

I agree that being able to easily debug generator performance would be useful, but I don't think it's obvious how this approach applies to some of the combinators, like gen/fmap and gen/bind. I'd have an easier time considering a more concrete proposal, that includes details for those kinds of cases.

Comment by JAre [ 24/Feb/19 9:13 PM ]

but I don't think it's obvious how this approach applies to some of the combinators, like gen/fmap

The metadata shouldn't be available from withing the function that gen/fmap applies. Instead the time spent inside the function and other performance stats should be assoc-ed with the gen/fmap inputs stats and then returned attached to the outputs. So the data will be carried by the context between nested gen/ calls.

The metadata shouldn't be available from withing the function that gen/fmap calls.

Alternatively you can allow to peak into the inputs stats but not alter it. Not sure about that. It may be too confusing.

Comment by JAre [ 24/Feb/19 9:20 PM ]

Actually if you allow peeking it might be used not only for debugging but also to alter the generation for performance.
For amortisation of the cumulative generator complexity, to meet some deadline by switching routes.

  • - - - -

@gfredericks For some reason I can't add a new comments to any issue so I update the old one.

For:

(def g1 ...)

(def g2 (gen/fmap f g1))

complexity function should output

(complexit g1) => {:name <generator-name> :time-ms 10}

(complexit g2) => {:name :fmap :time-ms 5 :args [{:name <generator-name> :time-ms 10}]}

here :time-ms 5 is time spent inside f function.

Comment by Gary Fredericks [ 02/Mar/19 9:08 PM ]

For example, let's say I have:

(def g1 ...)

(def g2 (gen/fmap f g1))

When I generate a value from g2, what kind of information will I get? Will it be about g1, or about f, or about both somehow?

Comment by Gary Fredericks [ 09/Mar/19 5:40 PM ]

JAre I believe your comment permissions are fixed.

Okay, I can see that it might be a well-defined concept. It would be a non-trivial amount of work, though, and I'd want to be sure that there wouldn't be a major performance difference. If there is, we'd probably want a way to have it off by default, and make sure that there's not a performance hit even when off.

Comment by JAre [ 09/Mar/19 6:19 PM ]

Yeah works now. Thx.





[CTYP-314] Current version of core.typed does not work with current Clojure 1.9.0-alpha11 Created: 21/Aug/16  Updated: 03/Dec/17  Resolved: 31/Aug/16

Status: Closed
Project: core.typed
Component/s: Clojure Checker
Affects Version/s: 0.3.23
Fix Version/s: 0.3.25

Type: Defect Priority: Blocker
Reporter: lvh Assignee: Ambrose Bonnaire-Sergeant
Resolution: Completed Votes: 0
Labels: bug, spec


 Description   
WARNING: boolean? already refers to: #'clojure.core/boolean? in namespace: clojure.core.typed.contract-utils, being replaced by: #'clojure.core.typed.contract-utils/boolean?
Initializing core.typed ...
Exception in thread "main" java.lang.IllegalArgumentException: Call to clojure.core/fn did not conform to spec:
In: [0] val: clojure.core.unify/var-unify fails spec: :clojure.core.specs/arg-list at: [:args :bs :arity-1 :args] predicate: vector?
In: [0] val: clojure.core.unify/var-unify fails spec: :clojure.core.specs/args+body at: [:args :bs :arity-n] predicate: (cat :args :clojure.core.specs/arg-list :prepost (? map?) :body (* any?))
:clojure.spec/args  (clojure.core.unify/var-unify [G__5637 G__5638 G__5639 G__5640] (clojure.core/if-let [vb__5630__auto__ (G__5640 G__5638)] (clojure.core.unify/garner-unifiers G__5637 vb__5630__auto__ G__5639 G__5640) (clojure.core/if-let [vexpr__5631__auto__ (clojure.core/and (G__5637 G__5639) (G__5640 G__5639))] (clojure.core.unify/garner-unifiers G__5637 G__5638 vexpr__5631__auto__ G__5640) (if (clojure.core.unify/occurs? G__5637 G__5638 G__5639 G__5640) (throw (java.lang.IllegalStateException. (clojure.core/str "Cycle found in the path " G__5639))) (clojure.core.unify/bind-phase G__5640 G__5638 G__5639)))))
, compiling:(clojure/core/unify.clj:83:18)
       	at clojure.lang.Compiler.macroexpand1(Compiler.java:6795)
       	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6878)
       	at clojure.lang.Compiler.analyze(Compiler.java:6674)
       	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6880)
       	at clojure.lang.Compiler.analyze(Compiler.java:6674)
       	at clojure.lang.Compiler.access$300(Compiler.java:38)
       	at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:589)
...

This appears to be a consequence of following dependency tree:

[org.clojure/core.typed "0.3.23"]
   [org.clojure/core.contracts "0.0.4" :exclusions [[org.clojure/clojure]]]
     [org.clojure/core.unify "0.5.3"]

... that is: it's unify that's broken, being pulled in through core.contracts.



 Comments   
Comment by Alex Miller [ 22/Aug/16 1:40 PM ]

New versions of core.unify and core.contracts have been released that fix the issue.

Comment by Ambrose Bonnaire-Sergeant [ 26/Aug/16 10:02 PM ]

Thanks Alex. That fixed this issue, a very old version of CLJS is now failing the 'ns' spec.

I'll make time to update. (progress: https://github.com/typedclojure/core.typed/pull/105)

Comment by Ambrose Bonnaire-Sergeant [ 31/Aug/16 4:03 AM ]

Fixed in 0.3.25.





[CLJS-3060] Generative test involving persistent queue reversed order Created: 14/Mar/19  Updated: 14/Mar/19

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec


 Description   

The following arranges to test that conjing an item onto a persistent queue results in that item being last in the queue:

(require '[clojure.spec.alpha :as s]
         '[clojure.spec.gen.alpha :as gen]
         '[clojure.spec.test.alpha :as st]
         '[clojure.test.check]
         '[clojure.test.check.properties])

(s/def ::int-queue (s/coll-of int?
                              :gen #(gen/fmap
                                      (fn [x] (into #?(:cljs #queue [] :clj clojure.lang.PersistentQueue/EMPTY) x))
                                      (s/gen (s/coll-of int? :gen-max 5)))))

(defn enqueue [int-queue n]
  (conj int-queue n))

(s/fdef enqueue
        :args (s/cat :int-queue ::int-queue :n int?)
        :ret ::int-queue
        :fn #(= (last (:ret %)) (:n (:args %))))

(st/check `enqueue)

Running this in Clojure works fine. In ClojureScript 1.10.520 you'll get a spec failure and it appears from the error report, the item is at the front of the queue. Here is an example:

[{:spec #object[cljs.spec.alpha.t_cljs$spec$alpha3986], :clojure.spec.test.check/ret 
{:shrunk {:total-nodes-visited 4, :depth 1, :pass? false, :result #error 
{:message "Specification-based check failed", :data {:cljs.spec.alpha/problems 
[{:path [:fn], :pred (cljs.core/fn [%] (cljs.core/= (cljs.core/last (:ret %)) 
(:n (:args %)))), :val {:args {:int-queue (0), :n -1}, :ret (-1 0)}, :via [], :in []}], 
:cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha3687], 
:cljs.spec.alpha/value {:args {:int-queue (0), :n -1}, :ret (-1 0)}, 
:cljs.spec.test.alpha/args (#queue [0] -1), :cljs.spec.test.alpha/val 
{:args {:int-queue (0), :n -1}, :ret (-1 0)}, :cljs.spec.alpha/failure :check-failed}}, 
:result-data #:clojure.test.check.properties{:error #error {:message 
"Specification-based check failed", :data {:cljs.spec.alpha/problems [{:path [:fn], 
:pred (cljs.core/fn [%] (cljs.core/= (cljs.core/last (:ret %)) (:n (:args %)))), 
:val {:args {:int-queue (0), :n -1}, :ret (-1 0)}, :via [], :in []}],
 :cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha3687], 
:cljs.spec.alpha/value {:args {:int-queue (0), :n -1}, :ret (-1 0)}, 
:cljs.spec.test.alpha/args (#queue [0] -1), :cljs.spec.test.alpha/val 
{:args {:int-queue (0), :n -1}, :ret (-1 0)}, :cljs.spec.alpha/failure :check-failed}}}, 
:time-shrinking-ms 4, :smallest [(#queue [0] -1)]}, :failed-after-ms 4, 
:num-tests 2, :seed 1552588512350, :fail [(#queue [0 0] -1)], :result #error 
{:message "Specification-based check failed", :data {:cljs.spec.alpha/problems 
[{:path [:fn], :pred (cljs.core/fn [%] (cljs.core/= (cljs.core/last (:ret %)) 
(:n (:args %)))), :val {:args {:int-queue (0 0), :n -1}, :ret (-1 0 0)}, :via [], :in []}], 
:cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha3687], 
:cljs.spec.alpha/value {:args {:int-queue (0 0), :n -1}, :ret (-1 0 0)},
 :cljs.spec.test.alpha/args (#queue [0 0] -1), :cljs.spec.test.alpha/val {:args 
{:int-queue (0 0), :n -1}, :ret (-1 0 0)}, :cljs.spec.alpha/failure :check-failed}}, 
:result-data #:clojure.test.check.properties{:error #error {:message 
"Specification-based check failed", :data {:cljs.spec.alpha/problems [{:path [:fn], 
:pred (cljs.core/fn [%] (cljs.core/= (cljs.core/last (:ret %)) (:n (:args %)))), 
:val {:args {:int-queue (0 0), :n -1}, :ret (-1 0 0)}, :via [], :in []}], 
:cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha3687], 
:cljs.spec.alpha/value {:args {:int-queue (0 0), :n -1}, :ret (-1 0 0)}, 
:cljs.spec.test.alpha/args (#queue [0 0] -1), :cljs.spec.test.alpha/val {:args 
{:int-queue (0 0), :n -1}, :ret (-1 0 0)}, :cljs.spec.alpha/failure :check-failed}}}, 
:failing-size 1, :pass? false}, :sym cljs.user/enqueue, :failure #error 
{:message "Specification-based check failed", :data {:cljs.spec.alpha/problems 
[{:path [:fn], :pred (cljs.core/fn [%] (cljs.core/= (cljs.core/last (:ret %)) 
(:n (:args %)))), :val {:args {:int-queue (0), :n -1}, :ret (-1 0)}, :via [], :in []}], 
:cljs.spec.alpha/spec #object[cljs.spec.alpha.t_cljs$spec$alpha3687], 
:cljs.spec.alpha/value {:args {:int-queue (0), :n -1}, :ret (-1 0)}, 
:cljs.spec.test.alpha/args (#queue [0] -1), :cljs.spec.test.alpha/val {:args 
{:int-queue (0), :n -1}, :ret (-1 0)}, :cljs.spec.alpha/failure :check-failed}}}]





[CLJS-3050] Clojurescript not using custom explain-out when macro invocation fails spec Created: 08/Feb/19  Updated: 11/Feb/19

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Ben Brinckerhoff Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: spec
Environment:

Clojurescript 1.10.516, Clojure 1.10



 Description   

This is a follow-up to https://dev.clojure.org/jira/browse/CLJS-2913

;; start with
;; clj -Srepro -Sdeps '{:deps {org.clojure/test.check {:mvn/version "0.9.0"} org.clojure/clojurescript {:mvn/version "1.10.516"}}}' --main cljs.main --repl

(require '[cljs.spec.alpha :as s])
(require '[cljs.spec.test.alpha :as st])
(require '[clojure.core.specs.alpha])

;; instrumenting a function
(s/fdef foobar :args (s/cat :x int?))
(defn foobar [x] x)

(st/instrument)

;; set up an example spec printer
(set! s/*explain-out* (fn [e-data] (println "SPEC ERROR!")))

;;This works great, thanks for fixing! Note custom printer is used.
(foobar "")
;; Execution error - invalid arguments to cljs.user/foobar at (<cljs repl>:1).
;; SPEC ERROR!

;; Expected: should call custom explain-out e.g. output should include "SPEC ERROR"
;; Actual: Uses default explain-out provided by spec
(let "")
;; Execution error - invalid arguments to cljs.analyzer/do-macroexpand-check at (analyzer.cljc:3772).
;; val: "" fails spec: :cljs.core.specs.alpha/bindings at: [:args :bindings] predicate: vector?


 Comments   
Comment by Thomas Heller [ 09/Feb/19 4:10 AM ]

Macros are checked and expanded as part of the compilation on the JVM side and the generated JS code is never eval'd in that JVM. It is not possible to change this binding from CLJS directly and would need to be done outside the compilation from CLJ.

Comment by Ben Brinckerhoff [ 09/Feb/19 7:27 PM ]

That's a really good point. Does this mean that error printing for specs is fundamentally unable to be configured by users for CLJS?

I wonder if there's another approach that would allow me to start up a CLJS REPL with custom error printing when macros don't match the specs.

Comment by Michiel Borkent [ 10/Feb/19 2:41 AM ]

Note that in self-hosted CLJS it does work. E.g. with planck:

cljs.user=> (let "")
Syntax error macroexpanding cljs.core$macros/let at (<cljs repl>:1:1).
SPEC ERROR!
Comment by Michiel Borkent [ 10/Feb/19 2:44 AM ]

You can set the custom printer in the JVM as follows:

$ clj -Srepro -Sdeps '{:deps {org.clojure/test.check {:mvn/version "0.9.0"} org.clojure/clojurescript {:mvn/version "1.10.516"}}}' -e '(set! clojure.spec.alpha/*explain-out* (fn [e-data] (println "SPEC ERROR!")))' -m cljs.main -re node

#object[user$eval1$fn__137 0x65045a87 "user$eval1$fn__137@65045a87"]
ClojureScript 1.10.516
(require '[cljs.spec.alpha :as s])
(require '[cljs.spec.test.alpha :as st])
nil
cljs.user=> nil
cljs.user=> (require '[clojure.core.specs.alpha])
nil
cljs.user=> (let "")
Syntax error macroexpanding cljs.core/let at (<cljs repl>:1:1).
SPEC ERROR!
Comment by Ben Brinckerhoff [ 11/Feb/19 8:19 AM ]

Nice! I'll update the Expound documentation to include this technique.

Comment by Ben Brinckerhoff [ 11/Feb/19 8:20 AM ]

I think this means this bug can be closed (as others pointed out, it's an expected behavior in CLJS), but I'll leave it to the maintainers to determine if perhaps there are any considerations here (perhaps documentation changes?). Not sure.





[CLJS-3049] Undefined fdef is still present in result of (stest/checkable-syms) Created: 06/Feb/19  Updated: 13/Feb/19  Resolved: 13/Feb/19

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Michiel Borkent Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-3049.patch    
Patch: Code and Test
Approval: Accepted

 Description   

When looking into the code of CLJS-2844, I noticed that there isn't special handling of nil here:
https://github.com/clojure/clojurescript/blob/master/src/main/cljs/cljs/spec/alpha.cljc#L71
This has the result that after undefining a spec, it's still present in the result of (stest/checkable-syms).

Example:

(require '[clojure.spec.alpha :as s])
(require '[clojure.spec.test.alpha :as stest])
(defn foo [x] x)
(s/fdef foo :args (s/cat :x number?) :ret number?)
(stest/checkable-syms) ;;=> #{cljs.user/foo}
(s/def foo nil)
(stest/checkable-syms) ;;=> #{cljs.user/foo}

Patch CLJS-3049.patch fixes this and provides a test for it.



 Comments   
Comment by David Nolen [ 13/Feb/19 10:27 AM ]

fixed https://github.com/clojure/clojurescript/commit/ee1d2970a4fc349c5d4da39cf3d5867ad36de403





[CLJS-3046] [core.specs] Destructuring spec is overly restrictive in namespaced :keys Created: 31/Jan/19  Updated: 31/Jan/19

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec


 Description   

Port CLJ-2473 to ClojureScript.






[CLJS-3033] Maintain backward compatibility to keyword for passing clojure.test.check options Created: 07/Jan/19  Updated: 07/Jan/19  Resolved: 07/Jan/19

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Blocker
Reporter: Michiel Borkent Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-3033.patch    
Patch: Code and Test

 Description   

CLJS-2964 unified the keyword for passing clojure.test.check options with
Clojure. This introduced a breaking change. This commit unbreaks that change by
maintaining backward compatibility, supporting both the unified :clojure.spec.test.check/opts and old :clojure.test.check/opts keyword.



 Comments   
Comment by David Nolen [ 07/Jan/19 3:39 PM ]

fixed https://github.com/clojure/clojurescript/commit/ef56db9be1afdd09487cd680b9e182bcf69b0f8c





[CLJS-3029] Fail fast for s/with-gen on undefined spec Created: 31/Dec/18  Updated: 31/Dec/18

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec


 Description   

Observe what happens in Clojure if you attempt to use s/with-gen on an undefined spec:

Clojure 1.10.0
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/def ::foo (s/with-gen ::bar #(s/gen #{:a})))
Syntax error (IllegalArgumentException) compiling at (REPL:1:14).
No implementation of method: :specize* of protocol: #'clojure.spec.alpha/Specize found for class: nil

In ClojureScript, on the other hand, things don't fail fast, and when they fail later the error can be somewhat cryptic to developers:

$ clj -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.439"}}}' -m cljs.main
ClojureScript 1.10.439
cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (s/def ::foo (s/with-gen ::bar #(s/gen #{:a})))
:cljs.user/foo
cljs.user=> (s/valid? ::foo 10)
TypeError: null is not an object (evaluating 'self__.pred.call')
	 cljs$spec$alpha$Spec$conform_STAR_$arity$2 (cljs/spec/alpha.cljs:526:35)
	 cljs.spec.alpha/conform* (cljs/spec/alpha.cljs:40:14)
	 cljs$core$IFn$_invoke$arity$2 (cljs/spec/alpha.cljs:375:22)
	 cljs$spec$alpha$valid_QMARK_ (cljs/spec/alpha.cljs:371:1)
cljs.user=>





[CLJS-3023] Instrumenting next gives maximum call stack size exceeded Created: 20/Dec/18  Updated: 07/Jan/19  Resolved: 07/Jan/19

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Michiel Borkent Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-3023-2.patch     Text File CLJS-3023-3.patch     Text File CLJS-3023-4.patch     Text File CLJS-3023.patch    
Patch: Code and Test
Approval: Accepted

 Description   

rest and last can be successfully instrumented, but next seems to be vulnerable to a maximum call stack size exceeded error.

Repro:

$ clj -Srepro -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.439"} org.clojure/test.check {:mvn/version "RELEASE"}}}' -m cljs.main -re node -r
ClojureScript 1.10.439
cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (require '[clojure.spec.test.alpha :as stest])
nil
cljs.user=> (s/fdef clojure.core/next :args (s/cat :coll seqable?))
cljs.core/next
cljs.user=> (stest/instrument `next)
repl:42
throw e__6573__auto__;
^

RangeError: Maximum call stack size exceeded
    at /private/var/folders/7t/gpzjxhts24d5wvqys1dfvrd80000gn/T/out92072095452558798682044329212169952/cljs/spec/test/alpha.js:147:18
    at Object.G__47574__delegate (/private/var/folders/7t/gpzjxhts24d5wvqys1dfvrd80000gn/T/out92072095452558798682044329212169952/cljs/spec/test/alpha.js:181:27)
    at Object.G__47574 [as next] (/private/var/folders/7t/gpzjxhts24d5wvqys1dfvrd80000gn/T/out92072095452558798682044329212169952/cljs/spec/test/alpha.js:191:27)
    at Function.cljs.core.apply_to_simple.cljs$core$IFn$_invoke$arity$2 (/private/var/folders/7t/gpzjxhts24d5wvqys1dfvrd80000gn/T/out92072095452558798682044329212169952/cljs/core.js:13123:99)
    at Function.cljs.core.apply.cljs$core$IFn$_invoke$arity$2 (/private/var/folders/7t/gpzjxhts24d5wvqys1dfvrd80000gn/T/out92072095452558798682044329212169952/cljs/core.js:13405:34)
    at cljs$core$apply (/private/var/folders/7t/gpzjxhts24d5wvqys1dfvrd80000gn/T/out92072095452558798682044329212169952/cljs/core.js:13358:24)
    at /private/var/folders/7t/gpzjxhts24d5wvqys1dfvrd80000gn/T/out92072095452558798682044329212169952/cljs/spec/test/alpha.js:157:24
    at Object.G__47574__delegate (/private/var/folders/7t/gpzjxhts24d5wvqys1dfvrd80000gn/T/out92072095452558798682044329212169952/cljs/spec/test/alpha.js:181:27)
    at Object.G__47574 [as next] (/private/var/folders/7t/gpzjxhts24d5wvqys1dfvrd80000gn/T/out92072095452558798682044329212169952/cljs/spec/test/alpha.js:191:27)
    at Function.cljs.core.apply_to_simple.cljs$core$IFn$_invoke$arity$2 (/private/var/folders/7t/gpzjxhts24d5wvqys1dfvrd80000gn/T/out92072095452558798682044329212169952/cljs/core.js:13123:99)
(node:11768) [DEP0097] DeprecationWarning: Using a domain property in MakeCallback is deprecated. Use the async_context variant of MakeCallback or the AsyncResource class instead.
cljs.user=>

Patch CLJS-3023-2 provides a solution by going through the protocol method directly instead of using the function next in apply-to-simple. This seems reasonable, since this was already done to first as well.

Patch CLJS-3023-3 restores the fallback for seqs that do not implement INext.

Patch CLJS-3023-4 rebased on master.



 Comments   
Comment by Michiel Borkent [ 04/Jan/19 1:55 AM ]

CLJS-3023-2 improves the test by checking for more specific results.

Comment by Michiel Borkent [ 04/Jan/19 2:36 AM ]

This patch passes on canary: https://github.com/cljs-oss/canary/tree/results/reports/2019/01/04/job-000753-1.10.505-eb9e4d8

Comment by Michiel Borkent [ 07/Jan/19 10:17 AM ]

This patch restores the fallback for seqs that do not implement INext.

Comment by Michiel Borkent [ 07/Jan/19 3:47 PM ]

CLJS-3023-4 rebased on master.

Comment by David Nolen [ 07/Jan/19 4:17 PM ]

fixed https://github.com/clojure/clojurescript/commit/75e4e528f84a38b7bb9741a498f153457182a057





[CLJS-2995] Instrumented self-calling multi-arity fn throws maximum call stack exceeded with optimizations advanced Created: 29/Nov/18  Updated: 30/Nov/18  Resolved: 30/Nov/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Blocker
Reporter: Michiel Borkent Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2995.patch    
Patch: Code and Test
Approval: Accepted

 Description   

Multi-arity function cannot be instrumented and then run with optimizations advanced.

The self-call from arity-0 to another one is giving problems.
The self-call from another arity to arity-0 is also giving problems.

Repro:

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

(defn foo
  ([] (foo 0))
  ([a] (foo a 1))
  ([a b] [a b]))

(s/fdef foo
  :args (s/cat :a (s/? number?)
               :b (s/? number?)))

(defn -main [& args]
  (stest/instrument)
  (foo 1)
  (println "(foo 1) worked")
  (foo 1 2)
  (println "(foo 1 2) worked")
  (foo)
  (println "(foo) worked"))

(set! *main-cli-fn* -main)

Compile, run and output with advanced:

clj -Srepro -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.439"} org.clojure/test.check {:mvn/version "RELEASE"}}}' -m cljs.main -t nodejs -O advanced -co '{:pseudo-names true :pretty-print true}' -c repro
node out/main.js
(foo 1) worked
(foo 1 2) worked
/private/tmp/repro/out/main.js:2477
function $cljs$core$seq$$($coll$jscomp$37$$) {
                         ^

RangeError: Maximum call stack size exceeded
    at $cljs$core$seq$$ (/private/tmp/repro/out/main.js:2477:26)
    at $cljs$core$apply_to$$ (/private/tmp/repro/out/main.js:4916:26)
    at $cljs$core$apply$cljs$0core$0IFn$0_invoke$0arity$02$$ (/private/tmp/repro/out/main.js:5147:38)
    at Function.$cljs$core$IFn$_invoke$arity$0$ (/private/tmp/repro/out/main.js:19504:16)
    at $cljs$core$apply_to$$ (/private/tmp/repro/out/main.js:4918:78)
    at $cljs$core$apply$cljs$0core$0IFn$0_invoke$0arity$02$$ (/private/tmp/repro/out/main.js:5147:38)
    at Function.$cljs$core$IFn$_invoke$arity$0$ (/private/tmp/repro/out/main.js:19504:16)
    at $cljs$core$apply_to$$ (/private/tmp/repro/out/main.js:4918:78)
    at $cljs$core$apply$cljs$0core$0IFn$0_invoke$0arity$02$$ (/private/tmp/repro/out/main.js:5147:38)
    at Function.$cljs$core$IFn$_invoke$arity$0$ (/private/tmp/repro/out/main.js:19504:16)


 Comments   
Comment by Michiel Borkent [ 29/Nov/18 6:27 AM ]

Updated the repro with more general repro.

Comment by Michiel Borkent [ 29/Nov/18 8:15 AM ]

Narrowed it down the the call from 0-arity to 1-arity.

Comment by Michiel Borkent [ 29/Nov/18 9:03 AM ]

The self-call from arity-0 to another one is giving problems.
The self-call from another arity to arity-0 is also giving problems.

Comment by Michiel Borkent [ 29/Nov/18 10:09 AM ]

From Slack:

dnolen [4:42 PM]
I think we should rewind here a bit - after looking at the code - this is what I think
1) `ret` is for when we don't have arity info - just in case we get invoked via `.call` for some reason - it checks then defers to `f`
2) the individual arity methods on `ret` are only for advanced compilation - to provide a matching interface, but currently we call `apply` (slow) and then re-enter `ret` (loop) (edited)
3) instead of doing 2) we could do 1) - do not re-enter `ret` instead just check and invoke `f` (since we have arity info we can skip `apply`)
the only issue I see is code size, but we can fix that after we get it right
i.e. only produce dispatch cases for the ones we actually need, share the checking fn

mfikes [4:48 PM]
I agree. For step 3, it sounds like you are suggesting inlining (or perhaps making a call to shared) checking code in each static dispatch, and then after that, perhaps doing the same static dispatch on `f`. Critically, avoiding any call to `apply` which could lead to infinite loops (and generally difficult-to-reason-about logic, given that `apply` is another function altogether.)

dnolen [4:49 PM]
yep

and making it clear that path is always the same
one check then invoke the original

mfikes [4:52 PM]
Right here is the critical piece that would change. https://github.com/clojure/clojurescript/blob/7d3b94de959cafc3e56be5b869c57977119c51f3/src/main/cljs/cljs/spec/test/alpha.cljc#L297

mfikes [4:57 PM]
If we did this, this might be the only remaining use of `apply`, the 2nd line here. Maybe there is some way to avoid it as well and just use the 1st line unconditionally if we have all of the static dispatch cases covered. (This is not completely clear to me yet, but avoiding that `apply` as well would be cool, if we could pull it off.)
https://github.com/clojure/clojurescript/blob/7d3b94de959cafc3e56be5b869c57977119c51f3/src/main/cljs/cljs/spec/test/alpha.cljs#L113-L114

dnolen [4:58 PM]
@mfikes can't we invoke `applyTo`

mfikes [4:58 PM]
Oh, there is another one there: https://github.com/clojure/clojurescript/blob/7d3b94de959cafc3e56be5b869c57977119c51f3/src/main/cljs/cljs/spec/test/alpha.cljs#L127

Yeah, I suspect we are closer to killing this problematic bit of code :slightly_smiling_face:

The main theme appears to involve: Direct, simple logic, even if it leads to bloat, avoiding any reliance on `apply` if possible.

borkdude [5:01 PM]
this might also solve the (unimportant) issue of being unable to instrument `apply`

dnolen [5:01 PM]
yep

Comment by Michiel Borkent [ 30/Nov/18 3:57 AM ]

Patch attached.

Comment by Mike Fikes [ 30/Nov/18 7:44 AM ]

CLJS-2995.patch passes CI and Canary

Comment by Mike Fikes [ 30/Nov/18 7:56 AM ]

CLJS-2995.patch in patch-tender

Comment by David Nolen [ 30/Nov/18 1:09 PM ]

fixed https://github.com/clojure/clojurescript/commit/1525386b136a8f36bee2fabacfe87a3cd60254d4





[CLJS-2986] stest/check test doesn't work in self-hosted tests Created: 21/Nov/18  Updated: 21/Nov/18  Resolved: 21/Nov/18

Status: Resolved
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Michiel Borkent Assignee: Michiel Borkent
Resolution: Duplicate Votes: 0
Labels: spec

Attachments: Text File CLJS-2986.patch     Text File CLJS-2986-test.patch    
Patch: Code and Test

 Description   

The test in CLJS-2986-test.patch doesn't pass with script/test-self-parity.

The test:

(defn fn-2986 [x] true)
(s/fdef fn-2986 :args (s/cat :x int?) :ret true?)

(deftest test-2986
  (let [check-res
        (stest/check `fn-2986 {:clojure.test.check/opts {:num-tests 1}})]
    (is (seq check-res))
    (is (every? (fn [res]
                  (= 1 (-> res
                           :clojure.test.check/ret
                           :num-tests)))
                check-res))))

It fails with:

Testing cljs.spec.test-test

FAIL in (test-2986) (at cljs/test.js:429:14)
expected: (seq check-res)
  actual: (not (seq []))

I cannot reproduce this result with Planck.



 Comments   
Comment by Mike Fikes [ 21/Nov/18 2:23 PM ]

Is this a duplicate of CLJS-2955?

Comment by Michiel Borkent [ 21/Nov/18 2:31 PM ]

Mike: confirmed. I'll close this one.

Comment by Michiel Borkent [ 21/Nov/18 2:31 PM ]

Duplicate of CLJS-2955





[CLJS-2977] Spec instrumentation regression with varargs / :static-fns Created: 16/Nov/18  Updated: 30/Nov/18  Resolved: 30/Nov/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Blocker
Reporter: Matt Huebert Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: regression, spec
Environment:

1.10.439 (see repro)


Approval: Vetted

 Description   

With the following repl:

clj -Srepro -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.439"} org.clojure/test.check {:mvn/version "RELEASE"}}}' -m cljs.main -co '{:static-fns true}' -re node -r

..and code:

(require '[clojure.spec.alpha :as s])
   (require '[clojure.spec.test.alpha :as st])
   
   (defn defk [key & [doc]]
     key)

   (s/fdef defk
           :args (s/cat :key keyword?
                        :doc (s/? string?)))

   (st/instrument)

   (defk 1 1)

The last line should throw, but doesn't, if `:static-fns` is enabled.

This is a regression relative to 1.10.339. See CLJS-2793.



 Comments   
Comment by David Nolen [ 30/Nov/18 2:01 PM ]

fixed https://github.com/clojure/clojurescript/commit/ad07bbd3ed5c9108e156983ce4a1d289a0f768dc

Comment by Mike Fikes [ 30/Nov/18 2:28 PM ]

See CLJS-2997





[CLJS-2976] s/fdef docstring should refer to cljs.spec.test.alpha/check Created: 16/Nov/18  Updated: 26/Nov/18  Resolved: 26/Nov/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: docstring, newbie, spec

Attachments: Text File CLJS-2976.patch    
Patch: Code
Approval: Accepted

 Description   

The s/fdef docstring currently refers to cljs.spec.test.alpha/run-tests, a non-existent var, but if you look at the Clojure doc string you can see that this is meant to refer to cljs.spec.test.alpha/check.



 Comments   
Comment by Eugene Kostenko [ 26/Nov/18 9:38 AM ]

With the patch CLJS-2976.patch applied:

cljs.user=> (doc s/fdef)
-------------------------
cljs.spec.alpha/fdef
([fn-sym & specs])
Macro
  Takes a symbol naming a function, and one or more of the following:

  :args A regex spec for the function arguments as they were a list to be
    passed to apply - in this way, a single spec can handle functions with
    multiple arities
  :ret A spec for the function's return value
  :fn A spec of the relationship between args and ret - the
    value passed is {:args conformed-args :ret conformed-ret} and is
    expected to contain predicates that relate those values

  Qualifies fn-sym with resolve, or using *ns* if no resolution found.
  Registers an fspec in the global registry, where it can be retrieved
  by calling get-spec with the var or fully-qualified symbol.

  Once registered, function specs are included in doc, checked by
  instrument, tested by the runner cljs.spec.test.alpha/check, and (if
  a macro) used to explain errors during macroexpansion.

  Note that :fn specs require the presence of :args and :ret specs to
  conform values, and so :fn specs will be ignored if :args or :ret
  are missing.

  Returns the qualified fn-sym.

  For example, to register function specs for the symbol function:

  (s/fdef cljs.core/symbol
    :args (s/alt :separate (s/cat :ns string? :n string?)
                 :str string?
                 :sym symbol?)
    :ret symbol?)
Comment by Mike Fikes [ 26/Nov/18 9:41 AM ]

LGTM

Comment by Mike Fikes [ 26/Nov/18 11:28 AM ]

fixed https://github.com/clojure/clojurescript/commit/61fbb03f7ccd1e493ce0e4e7f697799e9aa8194b





[CLJS-2975] unstrument returns symbol of non-instrumented var Created: 16/Nov/18  Updated: 21/Nov/18  Resolved: 21/Nov/18

Status: Resolved
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Blocker
Reporter: Michiel Borkent Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2975.patch    
Patch: Code and Test

 Description   

Repro:

$ clj -Srepro -Sdeps '{:deps {org.clojure/clojurescript {:mvn/version "1.10.439"} org.clojure/test.check {:mvn/version "RELEASE"}}}' -m cljs.main -re node -r

ClojureScript 1.10.439
cljs.user=> (require '[clojure.spec.test.alpha :as stest])
nil
cljs.user=> (stest/instrument 'cljs.core/+)
[]
cljs.user=> (stest/unstrument)
[cljs.core/+]
cljs.user=>


 Comments   
Comment by David Nolen [ 21/Nov/18 4:51 AM ]

Isn't this just aliasing?

Comment by Michiel Borkent [ 21/Nov/18 5:59 AM ]

David:
In the example above, instrument returns an empty vector, which is correct because there is no fdef for cljs.core/+ (as far as I know).
The problem is that unstrument returns a vector with the symbol of something that wasn’t instrumented, which is incorrect.
The cljs.core / clojure.core aliasing is irrelevant to this. Updated the code to avoid this confusion.

Comment by Michiel Borkent [ 21/Nov/18 12:31 PM ]

Updated the test using a test-specific function.

Comment by Mike Fikes [ 21/Nov/18 7:37 PM ]

fixed https://github.com/clojure/clojurescript/commit/7d3b94de959cafc3e56be5b869c57977119c51f3





[CLJS-2971] Make cljs.spec.alpha/fn-sym private Created: 13/Nov/18  Updated: 26/Nov/18  Resolved: 26/Nov/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: newbie, spec

Attachments: Text File CLJS-2971.patch    
Patch: Code
Approval: Accepted

 Description   

cljs.spec.alpha/fn-sym was introduced with https://github.com/clojure/clojurescript/commit/20ba8ef9415b46c18172a59cfe63ad16d2a35a3c but wasn't meant to be public API. It should be converted to a defn-.



 Comments   
Comment by Mike Fikes [ 26/Nov/18 7:17 AM ]

LGTM and passes CI.

Comment by Mike Fikes [ 26/Nov/18 11:29 AM ]

fixed https://github.com/clojure/clojurescript/commit/e005b987a8d1fe96e76c2975b931575fc2bdfce3





[CLJS-2967] Make clojure.spec.alpha reloadable Created: 09/Nov/18  Updated: 30/Nov/18  Resolved: 30/Nov/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Michiel Borkent Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2967.patch    
Patch: Code
Approval: Accepted

 Description   

When reloading clojure.spec.alpha (stest/instrument) no longer instruments spec'ed functions.

Repro:

$ clj -m cljs.main -re node -r
cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (defn foo [x])
#'cljs.user/foo
cljs.user=> (s/fdef foo :args (s/cat :x int?))
cljs.user/foo
cljs.user=> (require '[clojure.spec.test.alpha :as stest])
nil
cljs.user=> (require '[clojure.spec.alpha :as s] :reload)
nil
cljs.user=> (stest/instrument)
[]


 Comments   
Comment by David Nolen [ 30/Nov/18 1:27 PM ]

fixed https://github.com/clojure/clojurescript/commit/8ceee7f839343c0675ee45882fd6b2ea9fe606bd





[CLJS-2966] Unable to eval some forms passed to instrument Created: 08/Nov/18  Updated: 08/Nov/18  Resolved: 08/Nov/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Declined Votes: 0
Labels: spec


 Description   

The following REPL sequence works in Clojure and also downstream in self-hosted ClojureScript Planck, but not in JVM ClojureScript:

$ clj -m cljs.main -re node -r
ClojureScript 1.10.439
cljs.user=> (require '[clojure.spec.test.alpha :as st])
nil
cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (defn foo [x] (inc x))
#'cljs.user/foo
cljs.user=> (defn bar [x] (inc x))
#'cljs.user/bar
cljs.user=> (s/fdef foo :args (s/cat :x int?))
cljs.user/foo
cljs.user=> (s/fdef bar :args (s/cat :x int?))
cljs.user/bar
cljs.user=> (st/instrumentable-syms)
#{cljs.user/foo cljs.user/bar}
cljs.user=> (disj (st/instrumentable-syms) `bar)
#{cljs.user/foo}
cljs.user=> (st/instrument (disj (st/instrumentable-syms) `bar))
clojure.lang.ExceptionInfo: Syntax error compiling at (1:16). at line 1 <cljs repl> {:file "<cljs repl>", :line 1, :column 1, :root-source-info {:source-type :fragment, :source-form (st/instrument (disj (st/instrumentable-syms) (quote cljs.user/bar)))}, :tag :cljs/analysis-error}
	at cljs.analyzer$error.invokeStatic(analyzer.cljc:718)
	at cljs.analyzer$error.invoke(analyzer.cljc:714)
	at cljs.analyzer$macroexpand_1.invokeStatic(analyzer.cljc:3594)
	at cljs.analyzer$macroexpand_1.invoke(analyzer.cljc:3590)
	at cljs.analyzer$analyze_seq.invokeStatic(analyzer.cljc:3627)
	at cljs.analyzer$analyze_seq.invoke(analyzer.cljc:3607)
	at cljs.analyzer$analyze_form.invokeStatic(analyzer.cljc:3810)
	at cljs.analyzer$analyze_form.invoke(analyzer.cljc:3807)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3860)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3851)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3880)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze_seq.invokeStatic(analyzer.cljc:3630)
	at cljs.analyzer$analyze_seq.invoke(analyzer.cljc:3607)
	at cljs.analyzer$analyze_form.invokeStatic(analyzer.cljc:3810)
	at cljs.analyzer$analyze_form.invoke(analyzer.cljc:3807)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3860)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3851)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3880)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3873)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3871)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze_let_binding_init.invokeStatic(analyzer.cljc:2062)
	at cljs.analyzer$analyze_let_binding_init.invoke(analyzer.cljc:2060)
	at cljs.analyzer$analyze_let_bindings_STAR_.invokeStatic(analyzer.cljc:2082)
	at cljs.analyzer$analyze_let_bindings_STAR_.invoke(analyzer.cljc:2071)
	at cljs.analyzer$analyze_let_bindings.invokeStatic(analyzer.cljc:2114)
	at cljs.analyzer$analyze_let_bindings.invoke(analyzer.cljc:2113)
	at cljs.analyzer$analyze_let.invokeStatic(analyzer.cljc:2130)
	at cljs.analyzer$analyze_let.invoke(analyzer.cljc:2124)
	at cljs.analyzer$fn__1898.invokeStatic(analyzer.cljc:2150)
	at cljs.analyzer$fn__1898.invoke(analyzer.cljc:2148)
	at clojure.lang.MultiFn.invoke(MultiFn.java:252)
	at cljs.analyzer$analyze_seq_STAR_.invokeStatic(analyzer.cljc:3600)
	at cljs.analyzer$analyze_seq_STAR_.invoke(analyzer.cljc:3598)
	at cljs.analyzer$analyze_seq_STAR__wrap.invokeStatic(analyzer.cljc:3605)
	at cljs.analyzer$analyze_seq_STAR__wrap.invoke(analyzer.cljc:3603)
	at cljs.analyzer$analyze_seq.invokeStatic(analyzer.cljc:3629)
	at cljs.analyzer$analyze_seq.invoke(analyzer.cljc:3607)
	at cljs.analyzer$analyze_form.invokeStatic(analyzer.cljc:3810)
	at cljs.analyzer$analyze_form.invoke(analyzer.cljc:3807)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3860)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3851)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3880)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze_seq.invokeStatic(analyzer.cljc:3630)
	at cljs.analyzer$analyze_seq.invoke(analyzer.cljc:3607)
	at cljs.analyzer$analyze_form.invokeStatic(analyzer.cljc:3810)
	at cljs.analyzer$analyze_form.invoke(analyzer.cljc:3807)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3860)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3851)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3880)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3873)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3871)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$parse_invoke_STAR_$ana_expr__2396.invoke(analyzer.cljc:3373)
	at clojure.core$mapv$fn__8436.invoke(core.clj:6892)
	at clojure.lang.PersistentList.reduce(PersistentList.java:141)
	at clojure.core$reduce.invokeStatic(core.clj:6807)
	at clojure.core$mapv.invokeStatic(core.clj:6883)
	at clojure.core$mapv.invoke(core.clj:6883)
	at cljs.analyzer$parse_invoke_STAR_.invokeStatic(analyzer.cljc:3374)
	at cljs.analyzer$parse_invoke_STAR_.invoke(analyzer.cljc:3322)
	at cljs.analyzer$parse_invoke.invokeStatic(analyzer.cljc:3380)
	at cljs.analyzer$parse_invoke.invoke(analyzer.cljc:3378)
	at cljs.analyzer$analyze_seq_STAR_.invokeStatic(analyzer.cljc:3601)
	at cljs.analyzer$analyze_seq_STAR_.invoke(analyzer.cljc:3598)
	at cljs.analyzer$analyze_seq_STAR__wrap.invokeStatic(analyzer.cljc:3605)
	at cljs.analyzer$analyze_seq_STAR__wrap.invoke(analyzer.cljc:3603)
	at cljs.analyzer$analyze_seq.invokeStatic(analyzer.cljc:3629)
	at cljs.analyzer$analyze_seq.invoke(analyzer.cljc:3607)
	at cljs.analyzer$analyze_form.invokeStatic(analyzer.cljc:3810)
	at cljs.analyzer$analyze_form.invoke(analyzer.cljc:3807)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3860)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3851)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3880)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3873)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3871)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$fn__1879.invokeStatic(analyzer.cljc:2041)
	at cljs.analyzer$fn__1879.invoke(analyzer.cljc:2037)
	at clojure.lang.MultiFn.invoke(MultiFn.java:252)
	at cljs.analyzer$analyze_seq_STAR_.invokeStatic(analyzer.cljc:3600)
	at cljs.analyzer$analyze_seq_STAR_.invoke(analyzer.cljc:3598)
	at cljs.analyzer$analyze_seq_STAR__wrap.invokeStatic(analyzer.cljc:3605)
	at cljs.analyzer$analyze_seq_STAR__wrap.invoke(analyzer.cljc:3603)
	at cljs.analyzer$analyze_seq.invokeStatic(analyzer.cljc:3629)
	at cljs.analyzer$analyze_seq.invoke(analyzer.cljc:3607)
	at cljs.analyzer$analyze_form.invokeStatic(analyzer.cljc:3810)
	at cljs.analyzer$analyze_form.invoke(analyzer.cljc:3807)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3860)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3851)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3880)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3873)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3871)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.analyzer$fn__1729$fn__1753.invoke(analyzer.cljc:1564)
	at cljs.analyzer$fn__1729.invokeStatic(analyzer.cljc:1564)
	at cljs.analyzer$fn__1729.invoke(analyzer.cljc:1511)
	at clojure.lang.MultiFn.invoke(MultiFn.java:252)
	at cljs.analyzer$analyze_seq_STAR_.invokeStatic(analyzer.cljc:3600)
	at cljs.analyzer$analyze_seq_STAR_.invoke(analyzer.cljc:3598)
	at cljs.analyzer$analyze_seq_STAR__wrap.invokeStatic(analyzer.cljc:3605)
	at cljs.analyzer$analyze_seq_STAR__wrap.invoke(analyzer.cljc:3603)
	at cljs.analyzer$analyze_seq.invokeStatic(analyzer.cljc:3629)
	at cljs.analyzer$analyze_seq.invoke(analyzer.cljc:3607)
	at cljs.analyzer$analyze_form.invokeStatic(analyzer.cljc:3810)
	at cljs.analyzer$analyze_form.invoke(analyzer.cljc:3807)
	at cljs.analyzer$analyze_STAR_.invokeStatic(analyzer.cljc:3860)
	at cljs.analyzer$analyze_STAR_.invoke(analyzer.cljc:3851)
	at cljs.analyzer$analyze.invokeStatic(analyzer.cljc:3880)
	at cljs.analyzer$analyze.invoke(analyzer.cljc:3863)
	at cljs.repl$evaluate_form$__GT_ast__6567.invoke(repl.cljc:517)
	at cljs.repl$evaluate_form.invokeStatic(repl.cljc:519)
	at cljs.repl$evaluate_form.invoke(repl.cljc:499)
	at cljs.repl$eval_cljs.invokeStatic(repl.cljc:689)
	at cljs.repl$eval_cljs.invoke(repl.cljc:682)
	at cljs.repl$repl_STAR_$read_eval_print__6752.invoke(repl.cljc:985)
	at cljs.repl$repl_STAR_$fn__6758$fn__6767.invoke(repl.cljc:1026)
	at cljs.repl$repl_STAR_$fn__6758.invoke(repl.cljc:1025)
	at cljs.compiler$with_core_cljs.invokeStatic(compiler.cljc:1416)
	at cljs.compiler$with_core_cljs.invoke(compiler.cljc:1405)
	at cljs.repl$repl_STAR_.invokeStatic(repl.cljc:988)
	at cljs.repl$repl_STAR_.invoke(repl.cljc:861)
	at cljs.cli$repl_opt.invokeStatic(cli.clj:314)
	at cljs.cli$repl_opt.invoke(cli.clj:301)
	at cljs.cli$main.invokeStatic(cli.clj:646)
	at cljs.cli$main.doInvoke(cli.clj:635)
	at clojure.lang.RestFn.applyTo(RestFn.java:139)
	at clojure.core$apply.invokeStatic(core.clj:667)
	at clojure.core$apply.invoke(core.clj:660)
	at cljs.main$_main.invokeStatic(main.clj:61)
	at cljs.main$_main.doInvoke(main.clj:52)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:705)
	at clojure.core$apply.invokeStatic(core.clj:665)
	at clojure.main$main_opt.invokeStatic(main.clj:469)
	at clojure.main$main_opt.invoke(main.clj:465)
	at clojure.main$main.invokeStatic(main.clj:576)
	at clojure.main$main.doInvoke(main.clj:539)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:705)
	at clojure.main.main(main.java:37)
Caused by: Syntax error compiling at (1:16).
Unable to resolve symbol: disj in this context
	at clojure.lang.Compiler.analyze(Compiler.java:6808)
	at clojure.lang.Compiler.analyze(Compiler.java:6745)
	at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3820)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7110)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.analyze(Compiler.java:6745)
	at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:6120)
	at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5467)
	at clojure.lang.Compiler$FnExpr.parse(Compiler.java:4029)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:7106)
	at clojure.lang.Compiler.analyze(Compiler.java:6789)
	at clojure.lang.Compiler.eval(Compiler.java:7175)
	at clojure.lang.Compiler.eval(Compiler.java:7133)
	at clojure.core$eval.invokeStatic(core.clj:3214)
	at clojure.core$eval.invoke(core.clj:3210)
	at cljs.spec.test.alpha$form__GT_sym_or_syms.invokeStatic(alpha.cljc:99)
	at cljs.spec.test.alpha$form__GT_sym_or_syms.invoke(alpha.cljc:90)
	at cljs.spec.test.alpha$instrument.invokeStatic(alpha.cljc:145)
	at cljs.spec.test.alpha$instrument.invoke(alpha.cljc:101)
	at clojure.lang.AFn.applyToHelper(AFn.java:165)
	at clojure.lang.AFn.applyTo(AFn.java:144)
	at clojure.core$apply.invokeStatic(core.clj:669)
	at clojure.core$apply.invoke(core.clj:660)
	at cljs.analyzer$macroexpand_1_STAR_$fn__2437.invoke(analyzer.cljc:3547)
	at cljs.analyzer$macroexpand_1_STAR_.invokeStatic(analyzer.cljc:3546)
	at cljs.analyzer$macroexpand_1_STAR_.invoke(analyzer.cljc:3533)
	... 149 more
Caused by: java.lang.RuntimeException: Unable to resolve symbol: disj in this context
	at clojure.lang.Util.runtimeException(Util.java:221)
	at clojure.lang.Compiler.resolveIn(Compiler.java:7415)
	at clojure.lang.Compiler.resolve(Compiler.java:7359)
	at clojure.lang.Compiler.analyzeSymbol(Compiler.java:7320)
	at clojure.lang.Compiler.analyze(Compiler.java:6768)
	... 174 more


 Comments   
Comment by Mike Fikes [ 08/Nov/18 10:01 AM ]

The only reason we are involving eval it to simplify the handling of quoted forms: https://github.com/clojure/clojurescript/commit/f2424b6b04d63694dcc050989b592566fee03992
In other words, it helps with massaging compile-time data, but it doesn't give us full dynamic evaluation capability.





[CLJS-2956] Stack overflow when specing core = Created: 03/Nov/18  Updated: 07/Dec/18  Resolved: 07/Dec/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Blocker
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec
Environment:

{:deps {org.clojure/clojurescript {:mvn/version "1.10.439"}
org.clojure/test.check {:mvn/version "0.10.0-alpha3"}}}


Attachments: Text File CLJS-2956-2.patch     Text File CLJS-2956-3.patch     Text File CLJS-2956.patch    
Patch: Code and Test

 Description   

This is a regression that does not occur with 1.10.339

$ clj -Srepro -m cljs.main
ClojureScript 1.10.439
cljs.user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as stest])
nil
cljs.user=> (s/fdef clojure.core/= :args (s/+ any?))
cljs.core/=
cljs.user=> (stest/instrument)
[cljs.core/=]
cljs.user=> (= 1)
RangeError: Maximum call stack size exceeded.
	 inode_assoc_BANG_ (cljs/core.cljs:7313:48)
	 assoc_BANG_ (cljs/core.cljs:7994:33)
	 cljs$core$ITransientAssociative$_assoc_BANG_$arity$3 (cljs/core.cljs:8061:37)
	 fromArrays (cljs/core.cljs:7940:35)
	 G__1959__delegate (cljs/spec/test/alpha.cljs:117:17)
	 cljs$lang$applyTo (cljs/spec/test/alpha.cljs:115:20)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3895:30)
	 cljs$core$apply (cljs/core.cljs:3887:1)
	 cljs.core/key-test (cljs/core.cljs:7067:12)
	 inode_assoc_BANG_ (cljs/core.cljs:7325:18)
	 assoc_BANG_ (cljs/core.cljs:7994:33)
	 cljs$core$ITransientAssociative$_assoc_BANG_$arity$3 (cljs/core.cljs:8061:37)
	 fromArrays (cljs/core.cljs:7940:35)
	 G__1959__delegate (cljs/spec/test/alpha.cljs:117:17)
	 cljs$lang$applyTo (cljs/spec/test/alpha.cljs:115:20)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3895:30)
	 cljs$core$apply (cljs/core.cljs:3887:1)
	 cljs.core/key-test (cljs/core.cljs:7067:12)
	 inode_assoc_BANG_ (cljs/core.cljs:7325:18)
	 assoc_BANG_ (cljs/core.cljs:7994:33)
	 cljs$core$ITransientAssociative$_assoc_BANG_$arity$3 (cljs/core.cljs:8061:37)
	 fromArrays (cljs/core.cljs:7940:35)
	 G__1959__delegate (cljs/spec/test/alpha.cljs:117:17)
	 cljs$lang$applyTo (cljs/spec/test/alpha.cljs:115:20)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3895:30)
	 cljs$core$apply (cljs/core.cljs:3887:1)
	 cljs.core/key-test (cljs/core.cljs:7067:12)
	 inode_assoc_BANG_ (cljs/core.cljs:7325:18)
	 assoc_BANG_ (cljs/core.cljs:7994:33)
	 cljs$core$ITransientAssociative$_assoc_BANG_$arity$3 (cljs/core.cljs:8061:37)
	 fromArrays (cljs/core.cljs:7940:35)
	 G__1959__delegate (cljs/spec/test/alpha.cljs:117:17)
	 cljs$lang$applyTo (cljs/spec/test/alpha.cljs:115:20)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3895:30)
	 cljs$core$apply (cljs/core.cljs:3887:1)
	 cljs.core/key-test (cljs/core.cljs:7067:12)
	 inode_assoc_BANG_ (cljs/core.cljs:7325:18)
	 assoc_BANG_ (cljs/core.cljs:7994:33)
	 cljs$core$ITransientAssociative$_assoc_BANG_$arity$3 (cljs/core.cljs:8061:37)
	 fromArrays (cljs/core.cljs:7940:35)
	 G__1959__delegate (cljs/spec/test/alpha.cljs:117:17)
	 cljs$lang$applyTo (cljs/spec/test/alpha.cljs:115:20)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3895:30)
	 cljs$core$apply (cljs/core.cljs:3887:1)
	 cljs.core/key-test (cljs/core.cljs:7067:12)
	 inode_assoc_BANG_ (cljs/core.cljs:7325:18)
	 assoc_BANG_ (cljs/core.cljs:7994:33)
	 cljs$core$ITransientAssociative$_assoc_BANG_$arity$3 (cljs/core.cljs:8061:37)
	 fromArrays (cljs/core.cljs:7940:35)
	 G__1959__delegate (cljs/spec/test/alpha.cljs:117:17)
	 cljs$lang$applyTo (cljs/spec/test/alpha.cljs:115:20)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3895:30)
	 cljs$core$apply (cljs/core.cljs:3887:1)
	 cljs.core/key-test (cljs/core.cljs:7067:12)
	 inode_assoc_BANG_ (cljs/core.cljs:7325:18)
	 assoc_BANG_ (cljs/core.cljs:7994:33)
	 cljs$core$ITransientAssociative$_assoc_BANG_$arity$3 (cljs/core.cljs:8061:37)
	 fromArrays (cljs/core.cljs:7940:35)
	 G__1959__delegate (cljs/spec/test/alpha.cljs:117:17)
	 cljs$lang$applyTo (cljs/spec/test/alpha.cljs:115:20)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3895:30)
	 cljs$core$apply (cljs/core.cljs:3887:1)
	 cljs.core/key-test (cljs/core.cljs:7067:12)
	 inode_assoc_BANG_ (cljs/core.cljs:7325:18)
	 assoc_BANG_ (cljs/core.cljs:7994:33)
	 cljs$core$ITransientAssociative$_assoc_BANG_$arity$3 (cljs/core.cljs:8061:37)
	 fromArrays (cljs/core.cljs:7940:35)
	 G__1959__delegate (cljs/spec/test/alpha.cljs:117:17)
	 cljs$lang$applyTo (cljs/spec/test/alpha.cljs:115:20)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3895:30)
	 cljs$core$apply (cljs/core.cljs:3887:1)
	 cljs.core/key-test (cljs/core.cljs:7067:12)
	 inode_assoc_BANG_ (cljs/core.cljs:7325:18)
	 assoc_BANG_ (cljs/core.cljs:7994:33)
	 cljs$core$ITransientAssociative$_assoc_BANG_$arity$3 (cljs/core.cljs:8061:37)
	 fromArrays (cljs/core.cljs:7940:35)
	 G__1959__delegate (cljs/spec/test/alpha.cljs:117:17)
	 cljs$lang$applyTo (cljs/spec/test/alpha.cljs:115:20)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3895:30)
	 cljs$core$apply (cljs/core.cljs:3887:1)
	 cljs.core/key-test (cljs/core.cljs:7067:12)
	 inode_assoc_BANG_ (cljs/core.cljs:7325:18)
	 assoc_BANG_ (cljs/core.cljs:7994:33)
	 cljs$core$ITransientAssociative$_assoc_BANG_$arity$3 (cljs/core.cljs:8061:37)
	 fromArrays (cljs/core.cljs:7940:35)
	 G__1959__delegate (cljs/spec/test/alpha.cljs:117:17)
	 cljs$lang$applyTo (cljs/spec/test/alpha.cljs:115:20)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3895:30)
	 cljs$core$apply (cljs/core.cljs:3887:1)
	 cljs$core$key_test (cljs/core.cljs:7067:12)
cljs.user=>


 Comments   
Comment by Mike Fikes [ 03/Nov/18 9:12 AM ]

The fundamental challenge, as I see it, is that since the spec-checking “wrapper” function is implemented using ClojureScript, and a lot of the JavaScript code that is emitted in order to support the function dispatch machinery is also implemented by calling on ClojureScript core functions, it becomes meta-circular very quickly when you introduce the concept of specing core functions—and sometimes leads to infinite recursion.

A corollary: Tickets similar to this one may only affect projects that attempt to spec core functions. “Regular” projects that spec their own non-core functions may be immune to this class of issues.

Comment by Mike Fikes [ 03/Nov/18 8:11 PM ]

Michiel Borkent noticed that the error occurs in the let binding of the with-instrument-disabled macro. And, in fact, the regression occurred with CLJS-2953.

The patch eliminates the var special use (which was causing some map construction in the generated code, ultimately calling =), and instead just uses interop to fetch the value. In addition to this being a bit more performant, it fixes the ticket as written.

Comment by Mike Fikes [ 24/Nov/18 8:19 PM ]

CLJS-2956-2.patch rebaselines

Comment by Mike Fikes [ 30/Nov/18 1:33 PM ]

CLJS-2956-3.patch rebaselines

Comment by David Nolen [ 07/Dec/18 12:59 PM ]

Let's ammend the patch to clarify why we made this change here by adding a code comment. Otherwise I'm ok with it.

Comment by Mike Fikes [ 07/Dec/18 5:24 PM ]

fixed https://github.com/clojure/clojurescript/commit/848e10a9dac539b9271d83577fc1266f18e949da





[CLJS-2955] Self-host: spec check macro compile-time expansion Created: 02/Nov/18  Updated: 26/Dec/18  Resolved: 26/Dec/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Blocker
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 1
Labels: bootstrap, spec

Attachments: Text File CLJS-2955-2.patch     Text File CLJS-2955-3.patch     Text File CLJS-2955.patch    
Patch: Code and Test
Approval: Accepted

 Description   

If you add the following test code to the cljs.spec.test-test namespace, you'll see that it works just fine in JVM ClojureScript:

(defn foo-1234 [n] "ret")

(s/fdef foo-1234
  :args (s/cat :n number?)
  :ret string?)

(deftest check-compile-time
  (is (seq (stest/check `foo-1234))))

But, in script/test-self-parity, you'll get a failure

FAIL in (check-compile-time) (at cljs/test.js:429:14)
expected: (seq (stest/check (quote cljs.spec.test-test/foo-1234)))
  actual: (not (seq []))


 Comments   
Comment by Mike Fikes [ 02/Nov/18 4:42 PM ]

The problem is that, under self-hosted, at compile time the stest/check macro expansion ends up consulting the runtime instead of compile-time spec registry ref atom. Thus, stest/check tends to work in a self-hosted REPL, but not for the example in the ticket description where it becomes critical that the compile-time data structure be consulted. The fix is a straightforward application of the similar technique applied in CLJS-1656.

Comment by Michiel Borkent [ 22/Nov/18 7:01 AM ]

CLJS-2964 (priority major) is currently waiting for this fix. Does it make sense to make the priority of this issue also major?

Comment by David Nolen [ 30/Nov/18 1:30 PM ]

The patch needs to be rebased.

Comment by Mike Fikes [ 30/Nov/18 1:49 PM ]

CLJS-2955-2.patch rebaselines

Comment by David Nolen [ 14/Dec/18 10:35 AM ]

This one needs to be rebased again.

Comment by Mike Fikes [ 14/Dec/18 1:57 PM ]

CLJS-2955-3.patch rebaselines

Comment by Mike Fikes [ 24/Dec/18 7:51 AM ]

fixed https://github.com/clojure/clojurescript/commit/341cf664ff18b316d74f1632c730893913b366df





[CLJS-2951] Add a spec generator for some? Created: 30/Oct/18  Updated: 30/Oct/18  Resolved: 30/Oct/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: newbie, spec


 Description   

Clojure has a spec generator for the core some? predicate:

$ clj
Clojure 1.9.0
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/exercise some?)
([#{} #{}] [() ()] [(Fi/jZ) (Fi/jZ)] [() ()] [{} {}] [() ()] [() ()] [[3/4 [-1.2890625 #uuid "8b1ca800-9eaf-48d3-b0d8-eed5bf972f42"]] [3/4 [-1.2890625 #uuid "8b1ca800-9eaf-48d3-b0d8-eed5bf972f42"]]] [((-4.25 false) {6 \9}) ((-4.25 false) {6 \9})] [#{{#uuid "d589d5a1-513a-455b-8c29-fe1fe718f284" 2/7} -1 [-7 -2.0]} #{{#uuid "d589d5a1-513a-455b-8c29-fe1fe718f284" 2/7} -1 [-7 -2.0]}])

But ClojureScript is lacking this:

$ clj -m cljs.main -re node -r
ClojureScript 1.10.339
cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (require 'clojure.test.check.generators)
nil
cljs.user=> (s/exercise some?)
repl:13
throw e__6464__auto__;
^

Error: Unable to construct gen at: [] for: function cljs$core$some_QMARK_(x){
return !((x == null));
}
    at cljs$spec$alpha$gensub (/private/var/folders/60/ndky5jxj3v3gw_7j1dwq7msw0000gn/T/out521309304431570920698843305844530/cljs/spec/alpha.js:995:8)
    at Function.cljs.spec.alpha.gen.cljs$core$IFn$_invoke$arity$2 (/private/var/folders/60/ndky5jxj3v3gw_7j1dwq7msw0000gn/T/out521309304431570920698843305844530/cljs/spec/alpha.js:1031:31)
    at cljs$spec$alpha$gen (/private/var/folders/60/ndky5jxj3v3gw_7j1dwq7msw0000gn/T/out521309304431570920698843305844530/cljs/spec/alpha.js:1017:28)
    at Function.cljs.spec.alpha.exercise.cljs$core$IFn$_invoke$arity$3 (/private/var/folders/60/ndky5jxj3v3gw_7j1dwq7msw0000gn/T/out521309304431570920698843305844530/cljs/spec/alpha.js:5439:61)
    at cljs$spec$alpha$exercise (/private/var/folders/60/ndky5jxj3v3gw_7j1dwq7msw0000gn/T/out521309304431570920698843305844530/cljs/spec/alpha.js:5419:33)
    at Function.cljs.spec.alpha.exercise.cljs$core$IFn$_invoke$arity$2 (/private/var/folders/60/ndky5jxj3v3gw_7j1dwq7msw0000gn/T/out521309304431570920698843305844530/cljs/spec/alpha.js:5433:33)
    at cljs$spec$alpha$exercise (/private/var/folders/60/ndky5jxj3v3gw_7j1dwq7msw0000gn/T/out521309304431570920698843305844530/cljs/spec/alpha.js:5415:33)
    at Function.cljs.spec.alpha.exercise.cljs$core$IFn$_invoke$arity$1 (/private/var/folders/60/ndky5jxj3v3gw_7j1dwq7msw0000gn/T/out521309304431570920698843305844530/cljs/spec/alpha.js:5429:33)
    at cljs$spec$alpha$exercise (/private/var/folders/60/ndky5jxj3v3gw_7j1dwq7msw0000gn/T/out521309304431570920698843305844530/cljs/spec/alpha.js:5411:33)
    at repl:1:104
cljs.user=>

Implementation guidance:

Here is the generator in Clojure:

https://github.com/clojure/spec.alpha/blob/2329bb2d869ce59d92a401082587cc82edfce95a/src/main/clojure/clojure/spec/gen/alpha.clj#L136

This would be added to ClojureScript around here:

https://github.com/clojure/clojurescript/blob/6353a9b381144d6d0caa621322af9587922e7c07/src/main/cljs/cljs/spec/gen/alpha.cljs#L92



 Comments   
Comment by David Nolen [ 30/Oct/18 11:41 AM ]

fixed https://github.com/clojure/clojurescript/commit/78a013a295442d272f3b6bbf2cf4f5b464ea222a





[CLJS-2942] spec fdef triggering for variadic fn Created: 21/Oct/18  Updated: 28/Oct/18  Resolved: 27/Oct/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Completed Votes: 1
Labels: spec
Environment:

{:deps {org.clojure/clojurescript {:mvn/version "1.10.339"}
org.clojure/test.check {:mvn/version "0.10.0-alpha3"}}}



 Description   

If you define a spec for a variadic function with a non-zero minimum fixed arity, it will incorrectly trigger:

$clj -Srepro -m cljs.main
ClojureScript 1.10.339
cljs.user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as st])

cljs.user=> (defn foo [x & ys])
#'cljs.user/foo
cljs.user=> (s/fdef foo :args (s/cat :x number? :ys (s/* number?)))
cljs.user/foo
cljs.user=> (st/instrument)
[cljs.user/foo]
cljs.user=> (foo 1)
#error {:message "Call to #'cljs.user/foo did not conform to spec:\nval: 1 fails at: [:args] predicate: (cat :x number? :ys (* number?))\n:cljs.spec.alpha/spec  #object[cljs.spec.alpha.t_cljs$spec$alpha1377]\n:cljs.spec.alpha/value  1\n:cljs.spec.alpha/args  1\n:cljs.spec.alpha/failure  :instrument\n", :data #:cljs.spec.alpha{:problems [{:path [:args], :pred (cljs.spec.alpha/cat :x cljs.core/number? :ys (cljs.spec.alpha/* cljs.core/number?)), :val 1, :via [], :in []}], :spec #object[cljs.spec.alpha.t_cljs$spec$alpha1377], :value 1, :args 1, :failure :instrument}}
	 cljs.core/ExceptionInfo (cljs/core.cljs:11129:11)
	 cljs$core$IFn$_invoke$arity$3 (cljs/core.cljs:11161:5)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:11159:16)
	 cljs$core$ex_info (cljs/core.cljs:11156:1)
	 G__4363__delegate (cljs/spec/test/alpha.cljs:110:40)
	 cljs.core/apply-to (cljs/core.cljs:3845:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3890:11)
	 cljs.core/apply (cljs/core.cljs:3883:1)
	 G__4366__delegate (cljs/spec/test/alpha.cljs:119:38)
	 G__4366 (cljs/spec/test/alpha.cljs:114:36)
	 G__10206__2 (cljs/core.cljs:2006:6)
	 G__10206 (cljs/core.cljs:1995:1)

Note that with master, you get a slightly different error message in the failure:

deps.edn
{:deps {org.clojure/clojurescript {:git/url "https://github.com/clojure/clojurescript" :sha "6eedd0a08c49f7b0d4dcb30977b2fb38c90577bd"}
        org.clojure/test.check {:mvn/version "0.10.0-alpha3"}}}
$ clj -Srepro -m cljs.main
cljs.user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as st])
nil
cljs.user=> (defn foo [x & ys])
#'cljs.user/foo
cljs.user=> (s/fdef foo :args (s/cat :x number? :ys (s/* number?)))
cljs.user/foo
cljs.user=> (st/instrument)
[cljs.user/foo]
cljs.user=> (foo 1)
#error {:message "Call to #'cljs.user/foo did not conform to spec.", :data #:cljs.spec.alpha{:problems [{:path [], :pred (cljs.core/fn [%] (cljs.core/or (cljs.core/nil? %) (cljs.core/sequential? %))), :val 1, :via [], :in []}], :spec #object[cljs.spec.alpha.t_cljs$spec$alpha21124], :value 1, :fn cljs.user/foo, :args 1, :failure :instrument}}
	 cljs.core/ExceptionInfo (cljs/core.cljs:11147:11)
	 cljs$core$IFn$_invoke$arity$3 (cljs/core.cljs:11179:5)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:11177:16)
	 cljs$core$ex_info (cljs/core.cljs:11174:1)
	 G__24320__delegate (cljs/spec/test/alpha.cljs:111:40)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__24323__delegate (cljs/spec/test/alpha.cljs:120:38)
	 G__24323 (cljs/spec/test/alpha.cljs:115:36)
	 G__17846__2 (cljs/core.cljs:2010:6)
	 G__17846 (cljs/core.cljs:1999:1)
cljs.user=>


 Comments   
Comment by Erik Assum [ 21/Oct/18 2:27 PM ]
21:24 $ clj -m cljs.main
cljs.user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as st])
nil
cljs.user=> (defn foo [& numbers] )
#'cljs.user/foo
cljs.user=> (s/fdef foo :args (s/cat :numbers (s/* number?)))
cljs.user/foo
cljs.user=> (st/instrument)
[cljs.user/foo]
cljs.user=> (foo)
RangeError: Maximum call stack size exceeded.
	 cljs.core/array-map-index-of (cljs/core.cljs:6551:4)
	 cljs$core$ILookup$_lookup$arity$3 (cljs/core.cljs:6831:16)
	 cljs$core$ILookup$_lookup$arity$2 (cljs/core.cljs:6828:14)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:1927:30)
	 cljs.core/get (cljs/core.cljs:1921:1)
	 cljs$spec$alpha$preturn (cljs/spec/alpha.cljs:1056:3)
	 cljs.spec.alpha/add-ret (cljs/spec/alpha.cljs:1102:23)
	 cljs.spec.alpha/preturn (cljs/spec/alpha.cljs:1065:15)
	 cljs.spec.alpha/re-conform (cljs/spec/alpha.cljs:1237:18)
	 cljs$spec$alpha$Spec$conform_STAR_$arity$2 (cljs/spec/alpha.cljs:1283:10)
	 cljs.spec.alpha/conform* (cljs/spec/alpha.cljs:40:14)
	 cljs$spec$alpha$conform (cljs/spec/alpha.cljs:153:4)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:111:40)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
	 cljs.core/apply-to (cljs/core.cljs:3848:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/core.cljs:3893:11)
	 cljs.core/apply (cljs/core.cljs:3886:1)
	 G__18928__delegate (cljs/spec/test/alpha.cljs:113:20)
cljs.user=>
Comment by Michiel Borkent [ 22/Oct/18 3:54 AM ]

Same error while instrumenting:

ClojureScript 1.10.339
cljs.user=> (require '[clojure.spec.test.alpha :as stest])
nil
cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (s/fdef clojure.core/apply
       #_=>   :args (s/and (fn [args]
       #_=>                  (ifn? (first args)))
       #_=>                (fn [args]
       #_=>                  (seqable? (last args)))))
cljs.core/apply
cljs.user=> (stest/instrument `apply)
Maximum call stack size exceeded.
Maximum call stack size exceeded.
Comment by Mike Fikes [ 25/Oct/18 7:04 AM ]

The experimental patch in CLJS-2793 fixes the issue as written along with the stack overflow examples provided by Erik and Michiel.

Comment by Mike Fikes [ 27/Oct/18 8:09 PM ]

Fixed with the patch in CLJS-2793. Stack overflow mentioned in comment broken out into CLJS-2948.

Comment by Michiel Borkent [ 28/Oct/18 3:20 AM ]

I'm testing with the latest master.

(require,'[clojure.spec.alpha,:as,s])
(require,'[clojure.spec.test.alpha,:as,stest])

(s/fdef clojure.core/merge
  :args (s/cat :maps (s/* (s/nilable associative?)))
  :ret (s/nilable associative?))

(stest/instrument `merge)

(merge) ;; should be nil, gives maximum call stack exceeded

Run with:

clj -A:test -m cljs.main -re nashorn -i maximum-call-stack-exceeded.cljs

Returns maximum call stack exceeded.

Comment by Michiel Borkent [ 28/Oct/18 4:22 AM ]

Tested by making a custom merge function called my-merge, so to see if spec didn't get in some kind of loop while having core merge instrumented. That didn't help.

Seems to work when there is one argument before the variadic one:

(require,'[clojure.spec.alpha,:as,s])
(require,'[clojure.spec.test.alpha,:as,stest])

(defn my-merge [m & maps]
  (when (some identity maps)
    (reduce #(conj (or %1 {}) %2) maps)))

(s/fdef my-merge
  :args (s/cat :first (s/nilable associative?) :maps (s/* (s/nilable associative?)))
  :ret (s/nilable associative?))

(stest/instrument `my-merge)

(my-merge {})




[CLJS-2940] Can't define nilable spec on undefined pred Created: 20/Oct/18  Updated: 30/Oct/18  Resolved: 30/Oct/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Blocker
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 2
Labels: spec


 Description   

This fails in ClojureScript but works in Clojure:

(s/def ::foo (s/cat :bar (s/nilable ::foo)))

Note: While this spec seems silly and is valid for things like [nil], [[nil]], it is just a minimal repro, where larger specs might exhibit a similar shape.

Repro:

$ clj -m cljs.main
ClojureScript 1.10.339
cljs.user=> (require '[clojure.spec.alpha :as s])

cljs.user=> (s/def ::foo (s/cat :bar (s/nilable ::foo)))
Error: Unable to resolve spec: :cljs.user/foo
	 cljs.spec.alpha/reg-resolve! (cljs/spec/alpha.cljs:71:18)
	 cljs$spec$alpha$Specize$specize_STAR_$arity$2 (cljs/spec/alpha.cljs:129:31)
	 cljs$core$IFn$_invoke$arity$2 (cljs/spec/alpha.cljs:124:18)
	 cljs.spec.alpha/specize* (cljs/spec/alpha.cljs:123:1)
	 cljs$core$IFn$_invoke$arity$2 (cljs/spec/alpha.cljs:142:30)
	 cljs.spec.alpha/specize (cljs/spec/alpha.cljs:140:1)
	 cljs$spec$alpha$nilable_impl (cljs/spec/alpha.cljs:1360:15)


 Comments   
Comment by Dom Kiva-Meyer [ 25/Oct/18 6:21 PM ]

It appears that s/nonconforming suffers from the same issue.

Comment by David Nolen [ 30/Oct/18 11:30 AM ]

fixed https://github.com/clojure/clojurescript/commit/267893a6ee9c8e558a6255fe408f9f38be5f8381





[CLJS-2892] throw ex-info on macroexpand spec error with ex-data Created: 04/Sep/18  Updated: 04/Sep/18  Resolved: 04/Sep/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Duplicate Votes: 0
Labels: spec


 Description   

Port https://github.com/clojure/spec.alpha/commit/b753c005a5e5301cbbe8400ba8987b339a08199d to ClojureScript.



 Comments   
Comment by Mike Fikes [ 04/Sep/18 9:55 PM ]

Subsumed into CLJS-2891.





[CLJS-2891] stop including data in ex-info message Created: 04/Sep/18  Updated: 06/Sep/18  Resolved: 06/Sep/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2891.patch    
Patch: Code
Approval: Accepted

 Description   

Port https://github.com/clojure/spec.alpha/commit/f23ea614b3cb658cff0044a027cacdd76831edcf to ClojureScript and subsume CLJS-2892 which introduced ex-info usage.



 Comments   
Comment by Mike Fikes [ 06/Sep/18 9:12 PM ]

fixed https://github.com/clojure/clojurescript/commit/5f0fabc65ae7ba201b32cc513a1e5931a80a2bf7





[CLJS-2890] fspec role in problem path is not useful Created: 04/Sep/18  Updated: 06/Sep/18  Resolved: 06/Sep/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2890.patch    
Patch: Code
Approval: Accepted

 Description   

Port CLJ-2392 to ClojureScript.



 Comments   
Comment by Mike Fikes [ 06/Sep/18 9:34 PM ]

fixed https://github.com/clojure/clojurescript/commit/47553d8a3173ad4ebfcbfc557b73ecb44ac468b6





[CLJS-2889] Improve sorting on problem printing Created: 04/Sep/18  Updated: 06/Sep/18  Resolved: 06/Sep/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2889.patch    
Patch: Code
Approval: Accepted

 Description   

Port CLJ-2393 to ClojureScript.



 Comments   
Comment by Mike Fikes [ 04/Sep/18 7:50 PM ]

Before:

cljs.user=> (let [[a :as b c] [1 2 3]] a)
clojure.lang.ExceptionInfo: Call to cljs.core/let did not conform to spec:
In: [0 0] val: [a :as b c] fails spec: :cljs.core.specs.alpha/local-name at: [:args :bindings :binding :sym] predicate: simple-symbol?
In: [0 0 3] val: (c) fails spec: :cljs.core.specs.alpha/seq-binding-form at: [:args :bindings :binding :seq] predicate: (cat :elems (* :cljs.core.specs.alpha/binding-form) :rest (? (cat :amp #{(quote &)} :form :cljs.core.specs.alpha/binding-form)) :as (? (cat :as #{:as} :sym :cljs.core.specs.alpha/local-name))),  Extra input
In: [0 0] val: [a :as b c] fails spec: :cljs.core.specs.alpha/map-bindings at: [:args :bindings :binding :map] predicate: map?
In: [0 0] val: [a :as b c] fails spec: :cljs.core.specs.alpha/map-special-binding at: [:args :bindings :binding :map] predicate: map?
 at line 1 <cljs repl> {:file "<cljs repl>", :line 1, :column 1, :root-source-info {:source-type :fragment, :source-form (let [[a :as b c] [1 2 3]] a)}, :tag :cljs/analysis-error}
Comment by Mike Fikes [ 04/Sep/18 8:04 PM ]

After:

For the particular example above, this involves Clojure generating the message, but after the change in ClojureScript, you can easily see the corresponding error update in self-hosted ClojureScript (in this case generated via Planck built with the change):

cljs.user=> (let [[a :as b c] [1 2 3]] a)
            ^
Call to cljs.core$macros/let did not conform to spec:
In: [0 0 3] val: (c) fails spec: :cljs.core.specs.alpha/seq-binding-form at: [:args :bindings :binding :seq] predicate: (cat :elems (* :cljs.core.specs.alpha/binding-form) :rest (? (cat :amp #{(quote &)} :form :cljs.core.specs.alpha/binding-form)) :as (? (cat :as #{:as} :sym :cljs.core.specs.alpha/local-name))),  Extra input
In: [0 0] val: [a :as b c] fails spec: :cljs.core.specs.alpha/local-name at: [:args :bindings :binding :sym] predicate: simple-symbol?
In: [0 0] val: [a :as b c] fails spec: :cljs.core.specs.alpha/map-bindings at: [:args :bindings :binding :map] predicate: map?
In: [0 0] val: [a :as b c] fails spec: :cljs.core.specs.alpha/map-special-binding at: [:args :bindings :binding :map] predicate: map?

The salient aspect is that the complaint about c bubbles up to the top.

Comment by Mike Fikes [ 06/Sep/18 9:11 PM ]

fixed https://github.com/clojure/clojurescript/commit/033d19cdaf53f979034c3b60329f71d1509204c9





[CLJS-2888] Printing of spec problems buries the failing predicate which should be more prominent Created: 04/Sep/18  Updated: 06/Sep/18  Resolved: 06/Sep/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2888.patch    
Patch: Code
Approval: Accepted

 Description   

Port CLJ-2391 to ClojureScript.



 Comments   
Comment by Mike Fikes [ 06/Sep/18 9:11 PM ]

fixed https://github.com/clojure/clojurescript/commit/2f73857f0af13fb386cedbc4da21f4e95e05fdfc





[CLJS-2887] Improve names in core macro specs Created: 04/Sep/18  Updated: 06/Sep/18  Resolved: 06/Sep/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2887.patch    
Patch: Code
Approval: Accepted

 Description   

Port CLJ-2395 to ClojureScript.



 Comments   
Comment by Mike Fikes [ 06/Sep/18 9:12 PM ]

fixed https://github.com/clojure/clojurescript/commit/ef32778989f7ba2311a1e8a5d99c30e6805f5719





[CLJS-2848] Default explain printer prints root val and spec Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2848.patch    
Patch: Code
Approval: Accepted

 Description   

Port CLJ-2171



 Comments   
Comment by Mike Fikes [ 03/Aug/18 2:48 PM ]

fixed https://github.com/clojure/clojurescript/commit/d2cc14af6b26cd06bbd4d871123d4dd82c8ca4e0





[CLJS-2847] s/coll-of and s/every gen is very slow if :kind specified without :into Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2847.patch    
Patch: Code
Approval: Accepted

 Description   

Port CLJ-2103.



 Comments   
Comment by Mike Fikes [ 21/Jul/18 5:18 PM ]

Before:

cljs.user=> (time (dorun (gen/sample (s/gen (s/coll-of int? :kind list?)) 1000)))
"Elapsed time: 18424.869714 msecs"

After:

cljs.user=> (time (dorun (gen/sample (s/gen (s/coll-of int? :kind list?)) 1000)))
"Elapsed time: 943.532827 msecs"
Comment by Mike Fikes [ 03/Aug/18 2:43 PM ]

fixed https://github.com/clojure/clojurescript/commit/a6885a0cb21274fcab180cc82c4b791ee96197c5





[CLJS-2846] [spec] s/tuple explain-data :pred problem Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2846.patch    
Patch: Code and Test
Approval: Accepted

 Description   

Port CLJ-2176



 Comments   
Comment by Mike Fikes [ 03/Aug/18 2:46 PM ]

fixed https://github.com/clojure/clojurescript/commit/6152a3165b3196cc25e0929f6b2367d2761f15ac





[CLJS-2845] [spec] generate random subsets of or'd required keys in map specs Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2845.patch    
Patch: Code and Test
Approval: Accepted

 Description   

Port CLJ-2046



 Comments   
Comment by Mike Fikes [ 03/Aug/18 2:39 PM ]

fixed https://github.com/clojure/clojurescript/commit/1261aed07d5e11b38d5deafcf6afa4a7abe4b346





[CLJS-2844] [spec] Add support for undefining a spec Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2844.patch    
Patch: Code and Test
Approval: Accepted

 Description   

Port CLJ-2060



 Comments   
Comment by Mike Fikes [ 03/Aug/18 2:33 PM ]

fixed https://github.com/clojure/clojurescript/commit/7187f8f91ee2cc35b41e22899c6103c86674dd2b





[CLJS-2843] [spec] s/explain of evaluated predicate yields :s/unknown Created: 21/Jul/18  Updated: 30/Oct/18  Resolved: 30/Oct/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Defect Priority: Blocker
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec


 Description   

Port CLJ-2068



 Comments   
Comment by David Nolen [ 30/Oct/18 2:09 PM ]

fixed https://github.com/clojure/clojurescript/commit/20ba8ef9415b46c18172a59cfe63ad16d2a35a3c





[CLJS-2842] [spec] Clarify s/every docstring for :kind Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: 1.10.238

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2842.patch    
Patch: Code
Approval: Accepted

 Description   

Port CLJ-2111



 Comments   
Comment by Mike Fikes [ 03/Aug/18 2:40 PM ]

fixed https://github.com/clojure/clojurescript/commit/387ae34f7d667853c6995b0f0c406455d02e9fb3





[CLJS-2841] [spec] instrument exception doesn't contain function name in ex-data Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2841.patch    
Patch: Code
Approval: Accepted

 Description   

Port CLJ-2166 to ClojureScript.



 Comments   
Comment by Mike Fikes [ 03/Aug/18 2:42 PM ]

fixed https://github.com/clojure/clojurescript/commit/4d408e1aae6961611200abb9f839838ebf6966a8





[CLJS-2840] [spec] s/keys explain-data :pred problem Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2840.patch    
Patch: Code and Test
Approval: Accepted

 Description   

Port CLJ-2177 to ClojureScript.



 Comments   
Comment by Mike Fikes [ 03/Aug/18 2:27 PM ]

fixed https://github.com/clojure/clojurescript/commit/e58cf89115f7f08354ba09caeb63eff08b142f01





[CLJS-2839] [spec] s/& explain-data :pred problem Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: 1.10.238

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2839.patch    
Patch: Code and Test
Approval: Accepted

 Description   

Port CLJ-2178 to ClojureScript.



 Comments   
Comment by Mike Fikes [ 03/Aug/18 2:20 PM ]

fixed https://github.com/clojure/clojurescript/commit/455724c99afb000d853af6838065d62ce6661d2b





[CLJS-2838] [spec] s/& does not check preds if regex matches empty collection Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2838.patch    
Patch: Code and Test
Approval: Accepted

 Description   

Port CLJ-2182 to ClojureScript.



 Comments   
Comment by Mike Fikes [ 03/Aug/18 2:18 PM ]

fixed https://github.com/clojure/clojurescript/commit/0ec3870d8e301f2d3c99c7dd9714f3fb101ba5c9





[CLJS-2837] [spec] `cat` specs should verify value is sequential Created: 21/Jul/18  Updated: 03/Aug/18  Resolved: 03/Aug/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: spec
Environment:

{:deps {org.clojure/clojurescript {:mvn/version "1.10.339"}}}


Attachments: Text File CLJS-2837.patch    
Patch: Code and Test
Approval: Accepted

 Description   

Port CLJ-2183 to ClojureScript.



 Comments   
Comment by Mike Fikes [ 21/Jul/18 10:48 AM ]

Note, the patch does not port the use of res to mres because doing so causes the predicate data to exhibit gensyms related to the anonymous function literal arguments.

Behavior with the patch:

cljs.user=> (require '[clojure.spec.alpha :as s])
nil
cljs.user=> (s/def ::kv (s/cat :k keyword? :v any?))
:cljs.user/kv
cljs.user=> (s/explain ::kv {"foo" "bar"})
val: {"foo" "bar"} fails spec: :cljs.user/kv predicate: (or (nil? %) (sequential? %))
:cljs.spec.alpha/spec  :cljs.user/kv
:cljs.spec.alpha/value  {"foo" "bar"}
Comment by Mike Fikes [ 03/Aug/18 2:09 PM ]

fixed https://github.com/clojure/clojurescript/commit/c40a475ff42273d4d355486104d56c1ec5d795d3





[CLJS-2828] cljs.core.specs.alpha: Check early if let binding vector is even Created: 16/Jul/18  Updated: 16/Jul/18

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Mike Fikes Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec


 Description   

When CLJ-2376 is applied, port it to ClojureScript.

This line can be updated to match the revision in the patch in CLJ-2376:
https://github.com/clojure/clojurescript/blob/17b71461ee6304e3696e397add1f4780525553fc/src/main/cljs/cljs/core/specs/alpha.cljc#L59






[CLJS-2758] Improving tracability of cljs.spec.test instrumented function call conform errors Created: 24/May/18  Updated: 19/Jun/18

Status: Open
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Christian Johansen Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: bug, enhancement, spec

Attachments: Text File 0001-Change-target-to-default-which-indicates-a-browser.patch     Text File 0002-Use-location.hostname-for-port-less-hostname.patch     Text File 0003-Infer-UA-product-for-error-parsing-from-error-stack-.patch     Text File 0004-Correct-namespace-for-locating-caller-in-spec.test.patch    
Patch: Code

 Description   

Context: Use `cljs.spec.test.alpha/instrument` to instrument `fdef`-ed functions. Call one of said functions with data that fails the argument spec. The thrown exception currently does not provide a lot of context:

1. The exception message contains "missing filename" and "missing line number" - ideally those should contain the file and line number of the offending call
2. The exception data is coded to include information about the caller, but the current implementation always yields `nil` here.

The result is that failure to conform to an fdef argument spec leaves no trace back to the call - it only tells you which function's spec was not satiesfied.

I've started to investigate, and found a few glitches, but I do not have a complete suggestion. I'm offering my patches so far, hoping to get some input on whether or not it would be desirable for me to continue investigating.

Here's what I found so far:

1. `target` was assumed to be `"browser"`, while it is documented to be `"default"` in browsers. Fixed in first patch
2. When attempting to parse the stack trace to find the caller, `host` included the port number. Adressed in the second patch
3. When attempting to parse the stack trace, `goog.userAgent.product` is used to determine the browser, but it is not reliable - for example, `product/IPHONE` is true when Chrome is in responsive design mode. This is both incorrect, and helpeless in the case of `cljs.spec.test.alpha`, which does not recognize iphones at all. Because the user agent is only used to parse stack traces, I fixed this by inferring the user agent from the stack trace instead.
4. The `find-caller` function used the wrong package name to find the `spec_checking_fn`. Additionally, it hard-coded the dots in the package name, while some browsers use `$`. I fixed this in the forth patch. I also moved creating the error outside of the `conform!` anonymous function, as placing it here removes `spec-checking-fn` from the stack trace.

With these patches, the caller is set reliably, although it doesn't seem to reflect the originating call-site. Also, the thrown exception still lacks file name and line number. I'd be happy to continue investigating, and add some tests if you are interested in eventually accepting these patches.

As a side-note: It seems to me that the function that infers user agent from stack trace more appropriately belongs in stacktrace.cljs, and I feel strongly that this should be the default behavior when no `user-agent-product` is provided (instead of the current: returning the stack trace string unmodified). I'll be happy to fix this as well, and add some tests, if the interest is there. Note that the current approach allows Safari to go down the Firefox route, which works like a charm.

Lastly: sorry if I put too many things in one place, I'll be happy to split up as instructed.



 Comments   
Comment by David Nolen [ 15/Jun/18 3:52 PM ]

Thanks! Have you submitted a CA?

Comment by Christian Johansen [ 19/Jun/18 4:21 AM ]

I sure have





[CLJS-2641] cljs.spec.alpha/fdef with s/* is broken Created: 09/Mar/18  Updated: 11/Mar/18  Resolved: 11/Mar/18

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.10.238
Fix Version/s: None

Type: Defect Priority: Critical
Reporter: Miikka Koskinen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec
Environment:

1.10.145



 Description   

In ClojureScript 1.10.145, the code below exceeds the call stack on the first call to foo. In ClojureScript 1.9.946 it works as expected (throws a spec exception on the second call to foo).

(ns foo.spec-instrument-test
  (:require [cljs.spec.alpha :as s]
            [cljs.spec.test.alpha :as stest]))

(enable-console-print!)

(defn foo [& args] (prn :got args))
(s/fdef foo :args (s/cat :args (s/* int?)))

(stest/instrument)
(foo 1 2 3)
(foo 1 :hello)
RangeError: Maximum call stack size exceeded
    at cljs.core.next (/Users/miikka/mess/2018-10/foo/out/main.js:526:476)
    at cljs.spec.alpha.deriv (/Users/miikka/mess/2018-10/foo/out/main.js:3698:381)
    at cljs.spec.alpha.re_conform (/Users/miikka/mess/2018-10/foo/out/main.js:3727:23)
    at undefined.cljs.spec.alpha.t_cljs$spec$alpha9065.cljs.spec.alpha.t_cljs$spec$alpha9065.cljs$spec$alpha$Spec$conform_STAR_$arity$2 (/Users/miikka/mess/2018-10/foo/out/main.js:3735:93)
    at cljs.spec.alpha.conform_STAR_ (/Users/miikka/mess/2018-10/foo/out/main.js:3433:117)
    at cljs.spec.alpha.conform (/Users/miikka/mess/2018-10/foo/out/main.js:3457:264)
    at /Users/miikka/mess/2018-10/foo/out/main.js:3806:485
    at Function.e [as cljs$core$IFn$_invoke$arity$variadic] (/Users/miikka/mess/2018-10/foo/out/main.js:3810:355)
    at Function.foo.spec_instrument_test.foo.cljs$lang$applyTo (/Users/miikka/mess/2018-10/foo/out/main.js:3850:316)
    at Function.cljs.core.apply.cljs$core$IFn$_invoke$arity$2 (/Users/miikka/mess/2018-10/foo/out/main.js:899:190)


 Comments   
Comment by Mike Fikes [ 09/Mar/18 6:56 AM ]

I got a stack overflow with the (foo 1 2 3) form.

"deps.edn"
{:deps {org.clojure/clojurescript {:mvn/version "1.10.145"}
        org.clojure/test.check {:mvn/version "0.10.0-alpha2"}}}
$ clj -Srepro -m cljs.main -re node
ClojureScript 1.10.145
cljs.user=> (require '[cljs.spec.alpha :as s]
 '[cljs.spec.test.alpha :as stest])
nil
cljs.user=> (defn foo [& args] (prn :got args))
#'cljs.user/foo
cljs.user=> (s/fdef foo :args (s/cat :args (s/* int?)))
cljs.user/foo
cljs.user=> (stest/instrument)
[cljs.user/foo]
cljs.user=> (foo 1 2 3)
repl:13
throw e__6325__auto__;
^

RangeError: Maximum call stack size exceeded
    at Array.join (native)
    at cljs$core$random_uuid (/private/var/folders/gx/nymj3l7x4zq3gxb97v2zwzb40000gn/T/out8891727756854429156473179676037935/cljs/core.js:36556:1625)
    at cljs$spec$alpha$rep_STAR_ (/private/var/folders/gx/nymj3l7x4zq3gxb97v2zwzb40000gn/T/out8891727756854429156473179676037935/cljs/spec/alpha.js:3707:457)
    at cljs$spec$alpha$deriv (/private/var/folders/gx/nymj3l7x4zq3gxb97v2zwzb40000gn/T/out8891727756854429156473179676037935/cljs/spec/alpha.js:4199:65)
    at cljs$spec$alpha$deriv (/private/var/folders/gx/nymj3l7x4zq3gxb97v2zwzb40000gn/T/out8891727756854429156473179676037935/cljs/spec/alpha.js:4189:214)
    at cljs$spec$alpha$re_conform (/private/var/folders/gx/nymj3l7x4zq3gxb97v2zwzb40000gn/T/out8891727756854429156473179676037935/cljs/spec/alpha.js:4600:48)
    at cljs.spec.alpha.regex_spec_impl.cljs.spec.alpha.t_cljs$spec$alpha3399.cljs$spec$alpha$Spec$conform_STAR_$arity$2 (/private/var/folders/gx/nymj3l7x4zq3gxb97v2zwzb40000gn/T/out8891727756854429156473179676037935/cljs/spec/alpha.js:4729:35)
    at cljs$spec$alpha$conform_STAR_ (/private/var/folders/gx/nymj3l7x4zq3gxb97v2zwzb40000gn/T/out8891727756854429156473179676037935/cljs/spec/alpha.js:36:13)
    at cljs$spec$alpha$conform (/private/var/folders/gx/nymj3l7x4zq3gxb97v2zwzb40000gn/T/out8891727756854429156473179676037935/cljs/spec/alpha.js:455:38)
    at /private/var/folders/gx/nymj3l7x4zq3gxb97v2zwzb40000gn/T/out8891727756854429156473179676037935/cljs/spec/test/alpha.js:129:41
cljs.user=>
Comment by Mike Fikes [ 09/Mar/18 7:34 AM ]

Git bisect result

93a841b6e1a043e4bac0fcae3d82cc0410f7f3fc is the first bad commit
commit 93a841b6e1a043e4bac0fcae3d82cc0410f7f3fc
Author: dnolen <dnolen@cognitect.com>
Date:   Fri Dec 22 15:09:57 2017 -0500

    CLJS-2397: Multi-arity function instrumentation fails with :static-fns true
    CLJS-2197: Calling instrumented var fails to check conformance

    The instrument var wrapper would copy over arity methods from the original
    fn but these would not be wrapped. Instead copy them over from a MetaFn
    that takes a validating fn.

:040000 040000 e29b8fb395420d5e56e608aa167a58ba2d90a032 f8019b280eaf16c2b1766cad2813c497d041ac04 M	src
Comment by Mike Fikes [ 09/Mar/18 2:54 PM ]

If foo is defined simply as (defn foo [a b c] (prn :got a b c)) then the original foo is called here

https://github.com/clojure/clojurescript/blob/92cbedd12cd39d6bc3083a269a10fba9daa7fc40/src/main/cljs/cljs/spec/test/alpha.cljs#L119

Instead with the variadic definition, that line jumps to calling the fn here,

https://github.com/clojure/clojurescript/blob/92cbedd12cd39d6bc3083a269a10fba9daa7fc40/src/main/cljs/cljs/spec/test/alpha.cljs#L107

which, in addition to causing the arguments to be conformed a second time, then the (apply f args) here

https://github.com/clojure/clojurescript/blob/92cbedd12cd39d6bc3083a269a10fba9daa7fc40/src/main/cljs/cljs/spec/test/alpha.cljs#L112

does the same, and we go into infinite recursion and stack overflow.

Comment by Mike Fikes [ 09/Mar/18 4:12 PM ]

As an experiment, inside the MetaFn replacing the (apply f args) with

(if-some [variadic (.-cljs$core$IFn$_invoke$arity$variadic f)]
  (variadic args)
  (apply f args))

works at the REPL for the ticket description but fails in advanced if you try to convert it to a test. This feels like a hack anyway, but it corroborates what is going on.

Comment by David Nolen [ 10/Mar/18 12:32 PM ]

The behavior is very unintuitive it appears to be due to JS dynamic binding.

Comment by A. R [ 11/Mar/18 5:23 AM ]

Just for the Jira records: CLJS-1663 is helpful in understanding this ticket.

Comment by David Nolen [ 11/Mar/18 1:11 PM ]

fixed https://github.com/clojure/clojurescript/commit/9dd4d41174079a568f27dcdf061568c985b2bd12





[CLJS-2417] Inter-ns s/fdef expansion side effect fails when load cached source Created: 24/Nov/17  Updated: 22/Dec/17  Resolved: 22/Dec/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.655, 1.9.908
Fix Version/s: None

Type: Defect Priority: Critical
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec


 Description   

Like CLJS-1989, but for inter-ns s/fdef as opposed to intra-ns. When loading from cache, if an s/fdef specs a function in another namespace, the load can fail. The root cause is that analysis cache writing and reading is scoped by namespace, and one failure mode is as follows: When analysis cache is written for a given namespace A, specs set up by a different namespace B may not have yet been loaded. When that namespace B is subsequently loaded, if any cache is written for B, it will, due to the scoping, only write out information relevant to B.

To repro, the setup is similar to that in CLJS-1989, but involves splitting the defn and s/fdef across two namespaces:

Have src/foo/core.cljs with:

(ns foo.core)

(defn bar [x])

Have src/baz/core.cljs with:

(ns baz.core
  (:require [clojure.spec.alpha :as s]
            [foo.core]))

(s/fdef foo.core/bar :args (s/cat :x number?))

Then:

$ java -cp cljs.jar:src clojure.main -m cljs.repl.nashorn
To quit, type: :cljs/quit
cljs.user=> (require '[clojure.spec.test.alpha :as st] 'baz.core)
true
cljs.user=> (st/instrument)
[foo.core/bar]
cljs.user=> :cljs/quit

Now this has been cached. If you exit and try again, (st/instrument) fails:

$ java -cp cljs.jar:src clojure.main -m cljs.repl.nashorn
To quit, type: :cljs/quit
cljs.user=> (require '[clojure.spec.test.alpha :as st] 'baz.core)
true
cljs.user=> (st/instrument)
[]

Note, the above was reproduced using ClojureScript 1.9.655 as well, where CLJS-1989 landed, to be sure that this wasn't a subsequent regression.

As an aside, the motivating example where this inter-ns pattern arises is porting of clojure.core.specs.alpha to cljs.core.specs.alpha (CLJS-2413) where in each case, the specs are in a separate *...specs namespace.



 Comments   
Comment by David Nolen [ 22/Dec/17 7:10 PM ]

fixed https://github.com/clojure/clojurescript/commit/9a54081d3e0ffab699ee2e58b2adc595918f7c2b





[CLJS-2414] Self-host: Macro specs are instrumented Created: 24/Nov/17  Updated: 01/Dec/17  Resolved: 01/Dec/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.908
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: bootstrap, spec

Attachments: Text File CLJS-2414-2.patch     Text File CLJS-2414.patch    
Patch: Code and Test

 Description   

If you use cljs.spec.alpha/fdef to define a macro spec, self-host will apply that spec as expected during macroexpansion time. But another unintended consequence is that cljs.spec.test.alpha/instrument will unnecessarily instrument this spec.



 Comments   
Comment by Mike Fikes [ 24/Nov/17 3:24 PM ]

The attached patch adds some self-host-conditional code that checks that a Var is not a macro before instrumenting it.
The patch also adds a test. Things pass if applied against master, but for the test to actually be executed in the self-hosted suite, CLJS-2415 will need to be applied.

Comment by David Nolen [ 01/Dec/17 3:40 PM ]

Looks like this one needs a rebase.

Comment by Mike Fikes [ 01/Dec/17 6:50 PM ]

Attaching re-baselined CLJS-2414-2.patch

Comment by David Nolen [ 01/Dec/17 7:03 PM ]

fixed https://github.com/clojure/clojurescript/commit/f55b19b89e98a210a89151f52e67567108c536cf





[CLJS-2413] Port core.specs.alpha to ClojureScript Created: 23/Nov/17  Updated: 22/Dec/17  Resolved: 22/Dec/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.908
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Mike Fikes Assignee: Mike Fikes
Resolution: Completed Votes: 2
Labels: spec

Attachments: Text File 0001-Experiment-Attach-ns-spec-to-internal-use-Var.patch     Text File CLJS-2413-1.patch    
Patch: Code
Approval: Accepted

 Description   

Port https://github.com/clojure/core.specs.alpha to ClojureScript, adding a cljs.core.specs.alpha namespace that has the specs in the Clojure version, modified as needed for ClojureScript. (We would ideally make minimal revisions, preserving as much original structure as reasonable, so that future upstream changes can be more easily ported. Additionally, tests should be ported over.)

The specs should be usable from both JVM-based and self-hosted ClojureScript. (This likely implies a design making clever use of cljc and reader-conditionals so that macro specs work for both ClojureScript variants.}

This would essentially be an experimental feature which, while shipping with ClojureScript, can be explicitly opted into by end-users who wish to make use of that namespace. In other words, it would not be required by any existing production code in the ClojureScript tree, at least initially. If it ships with ClojureScript, this can facilitate more broad experimentation and rooting out any fine tuning that may be needed in the specs.

We could add checks for the require and require-macros macros, heavily based on the support that exists in that namespace for the Clojure ns macro, but of course heavily modified to reflect ClojureScript's constraints and special features (string require, etc.)

We might also be able to use this work to to provide inspiration what to do about the subject of specing the ns special form. (Do we turn ns into a macro that expands to some other special form, or do we revise the compiler's macroexpand check to also be able to do special form checks?)



 Comments   
Comment by Thomas Heller [ 23/Nov/17 11:10 AM ]

FWIW I have been using some core.specs in shadow-cljs for a while now (just the JVM side though).

See:
https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/cljs/devtools/cljs_specs.clj

One major issue with these (or specs in general) is that the errors are absolutely terrible. Some checks done by the cljs.anaylzer or cljs.compiler provide far better error messages but don't test as strict. Lately I have been testing expound [1] which is a bit better but still far from good.

I'm also using spec to parse for the ns form which has the same issue but otherwise works very nice.
https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/build/ns_form.clj

[1] https://github.com/bhb/expound/

Comment by Mike Fikes [ 24/Nov/17 8:28 PM ]

The attached CLJS-2413-1.patch ports over the main namespace (there is no test namespace), making revisions for ClojureScript. Additionally, since we can't spec the ns special form, I added a few additional specs for require, require-macros, use, and use-macros.

Using the new namespace is easy: Just require cljs.core.specs.alpha and then specs will exist for:

cljs.core/defn
 cljs.core/defn-
 cljs.core/fn
 cljs.core/if-let
 cljs.core/import
 cljs.core/let
 cljs.core/require
 cljs.core/require-macros
 cljs.core/use
 cljs.core/use-macros
 cljs.core/when-let
Comment by Mike Fikes [ 25/Nov/17 9:15 AM ]

I tried some experiments with attaching a spec the ns special form:

Turning ns into a macro that expands to some other special form is challenging (I'm not convinced it is impossible, but you hit special circular cases when trying to bootstrap an environment this way). Perhaps this would be the "cleanest" solution, if it can be pulled off correctly.

Attempting to simply revise the macroexpand-check path to also work on special forms is problematic because you actually need a Var to attach a spec to.

The attached 0001-Experiment-Attach-ns-spec-to-internal-use-Var.patch, which applies after CLJS-2413-1.patch, illustrates a third potential approach: It introduces a internal-use macro Var to stand in for ns, attaches a spec to that internal-use Var, and then in the macroexpand-check path, special cases the ns special form to check any spec that might be attached to the internal-use Var.

With this experimental patch applied, you can indeed verify that ns forms can be spec-checked properly using the specs in that namespace.

Comment by David Nolen [ 22/Dec/17 11:24 AM ]

fixed https://github.com/clojure/clojurescript/commit/b11cbeefa5c148b256bcc0942d714c23ab4c6c81





[CLJS-2405] Register dumped specs fails Created: 20/Nov/17  Updated: 01/Dec/17  Resolved: 01/Dec/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.908
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2405.patch    
Patch: Code
Approval: Accepted

 Description   

When specs that are dumped into analysis cache files are registered, this fails (with a swallowed ClassCastException clojure.lang.PersistentVector cannot be cast to java.util.Map$Entry).

You can see evidence of this if you run script/noderepljs revised to set :verbose true: There is a spec, :cljs.spec.alpha/kvs->map, for which the code attempts to register, throws, with the file subsequently being re-analyzed anyway because the cache couldn't be loaded. The evidence is this being emitted by :verbose true:

Reading analysis cache for file:/Users/mfikes/Projects/clojurescript/src/main/cljs/cljs/spec/alpha.cljs
Analyzing file:/Users/mfikes/Projects/clojurescript/src/main/cljs/cljs/spec/alpha.cljs

At its core, it involves code like {{(merge {5 6} '([1 2] [3 4]))}}, which fails in Clojure.



 Comments   
Comment by Mike Fikes [ 20/Nov/17 11:44 AM ]

The patch revises it so at the bottom it involves a construct like {{(into {1 2, 3 4} '([1 7] [5 6]))}} which works in Clojure.

Comment by David Nolen [ 01/Dec/17 3:50 PM ]

fixed https://github.com/clojure/clojurescript/commit/b65563956e168a6a93acfe2e2482f6a42ca108fa





[CLJS-2397] Multi-arity function instrumentation fails with :static-fns true Created: 12/Nov/17  Updated: 22/Dec/17  Resolved: 22/Dec/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Jeaye Wilkerson Assignee: Mike Fikes
Resolution: Completed Votes: 0
Labels: cljs, spec
Environment:

GNU/Linux


Attachments: Text File CLJS-2397-1.patch     Text File CLJS-2397-2.patch    
Patch: Code

 Description   

The following code, in a cljc file, will pass all tests in Clojure, as well as ClojureScript with :optimizations :none, but will fail the second assertion when :optimizations :advanced is enabled.

(ns cljs-arity-spec-issue.core-test
  (:require #?@(:clj [[clojure.test :refer :all]
                      [clojure.spec.alpha :as s]
                      [clojure.spec.test.alpha :as st]]

              :cljs [[cljs.test
                      :refer-macros [deftest testing is use-fixtures]]
                     [cljs.spec.alpha :as s]
                     [cljs.spec.test.alpha :as st]])))

(defn arities
  ([a]
   (inc a))
  ([a b]
   (+ a b))
  ([a b c]
   0))
(s/fdef arities
        :args (s/or :arity-1 (s/cat :a number?)
                    :arity-2 (s/cat :a number? :b number?)
                    :arity-3 (s/cat :a string? :b boolean? :c map?))
        :ret number?)

(st/instrument)

(deftest arities'
  (testing "Arity-1 Positive"
    (is (arities 1)))
  (testing "Arity-1 Negative"
    (is (thrown? #?(:clj RuntimeException :cljs :default)
                 (arities "bad"))))) ; This test fails with :advanced enabled


 Comments   
Comment by Jeaye Wilkerson [ 12/Nov/17 6:10 PM ]

It may be helpful to note that I've found this is likely not related to the spec, but instead the defn itself. My reasoning is that, if the defn is changed to single-arity, while the spec is left the same, the issue goes away.

(ns cljs-arity-spec-issue.core-test
  (:require #?@(:clj [[clojure.test :refer :all]
                      [clojure.spec.alpha :as s]
                      [clojure.spec.test.alpha :as st]]

              :cljs [[cljs.test
                      :refer-macros [deftest testing is use-fixtures]]
                     [cljs.spec.alpha :as s]
                     [cljs.spec.test.alpha :as st]])))

(defn arities [a]
  (inc a))
(s/fdef arities ; Same spec, but only a single arity function.
        :args (s/or :arity-1 (s/cat :a number?)
                    :arity-2 (s/cat :a number? :b number?)
                    :arity-3 (s/cat :a string? :b boolean? :c map?))
        :ret number?)

(st/instrument)

(deftest arities' ; All tests will pass now.
  (testing "Arity-1 Positive"
    (is (arities 1)))
  (testing "Arity-1 Negative"
    (is (thrown? #?(:clj RuntimeException :cljs :default)
                 (arities "bad")))))
Comment by Thomas Heller [ 13/Nov/17 4:31 AM ]

I would suspect that is more likely related to :static-fns. Since the arity is known the compiler will directly dispatch to the appropriate fn instead of going though the dispatch fn which `fdef` might not cover?

Can you try with :none and :static-fns true?

Comment by Mike Fikes [ 13/Nov/17 5:55 AM ]

Minimal complete repro:

Put the following in src/hello_world/core.cljs:

(ns hello-world.core
  (:require [cljs.nodejs :as nodejs]
            [clojure.spec.alpha :as s]
            [clojure.spec.test.alpha :as st]))

(nodejs/enable-util-print!)

(defn arities
  ([a]
   (inc a))
  ([a b]
   (+ a b))
  ([a b c]
   0))

(s/fdef arities
        :args (s/or :arity-1 (s/cat :a number?)
                    :arity-2 (s/cat :a number? :b number?)
                    :arity-3 (s/cat :a string? :b boolean? :c map?))
        :ret number?)

(st/instrument)

(defn -main [& args]
  (try
    (prn (arities "bad"))
    (catch :default ex
      (prn ex))))

(set! *main-cli-fn* -main)

And this in node.clj:

(require 'cljs.build.api)

(cljs.build.api/build "src"
  {:main 'hello-world.core
   :output-to "main.js"
   :optimizations :none
   :static-fns true
   :target :nodejs})

With a copy of the shipping cljs.jar copied next to node.clj, compile with

java -cp cljs.jar:src clojure.main node.clj

and run with

node main.js

By revising node.clj and compling/running, you can see that it is :static-fns rather than :advanced. In particular, you can set :static-fns to false to override its setting under :advanced and instrumentation of this multi-arity function will work under :advanced.

As Thomas points out, this makes complete sense given the way that instrument works: It wraps the top-level function pointed to by the Var, not the individual direct-arity dispatch variants. (Take a look at how instrument-1 and instrument-1* work to set! the value of the Var to be a wrapped version of the original.)

It seems like this could either be fixed by detecting this situation and instead wrapping each of the dispatch variants, or by perhaps simply detecting when instrument is called while :static-fns is enabled and emitting a diagnostic (essentially indicating that this mode is unsupported).

Comment by Mike Fikes [ 14/Nov/17 10:10 AM ]

The attached CLJS-2397-1.patch is attached for discussion. It takes the approach of not fixing the issue by actually instrumenting each direct dispatch fn, but instead emitting a diagnostic and filtering for the problematic cases. It is odd in that it abuses things by triggering an analyzer warning, and it likewise doesn't attempt to change the return value of cljs.spec.test.alpha/instrumentable-vars, but it might be an approach with merit (especially, since instrumentation is often done during dev only).

Here is an illustration of how the patch behaves with :static-fns true:

ClojureScript Node.js REPL server listening on 50413
To quit, type: :cljs/quit
cljs.user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as st])
nil
cljs.user=> (defn f [x] 1)
#'cljs.user/f
cljs.user=> (s/fdef f :args any?)
cljs.user/f
cljs.user=> (defn g ([x] 1) ([x y] 2))
#'cljs.user/g
cljs.user=> (s/fdef g :args any?)
cljs.user/g
cljs.user=> (defn h ([x] 1) ([x & ys] 2))
#'cljs.user/h
cljs.user=> (s/fdef h :args any?)
cljs.user/h
cljs.user=> (st/instrument)
WARNING: cljs.user/h cannot be instrumented (it is variadic and :static-fns is enabled) in file <cljs repl>
WARNING: cljs.user/g cannot be instrumented (it is multi-arity and :static-fns is enabled) in file <cljs repl>
[cljs.user/f]

Note that, in the above, the return value for st/instruement only reflects that f has been instrumented.

Here is the same without :static-fns true:

ClojureScript Node.js REPL server listening on 51318
To quit, type: :cljs/quit
cljs.user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as st])
nil
cljs.user=> (defn f [x] 1)
#'cljs.user/f
cljs.user=> (s/fdef f :args any?)
cljs.user/f
cljs.user=> (defn g ([x] 1) ([x y] 2))
#'cljs.user/g
cljs.user=> (s/fdef g :args any?)
cljs.user/g
cljs.user=> (defn h ([x] 1) ([x & ys] 2))
#'cljs.user/h
cljs.user=> (s/fdef h :args any?)
cljs.user/h
cljs.user=> (st/instrument)
[cljs.user/h cljs.user/f cljs.user/g]
Comment by Mike Fikes [ 14/Dec/17 2:51 PM ]

Attaching re-baselined CLJS-2397-2.patch

Comment by David Nolen [ 22/Dec/17 2:10 PM ]

fixed https://github.com/clojure/clojurescript/commit/93a841b6e1a043e4bac0fcae3d82cc0410f7f3fc





[CLJS-2387] CLJS Analyzer does not correctly detect cache hits for analyzed spec files Created: 19/Oct/17  Updated: 01/Dec/17  Resolved: 01/Dec/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Alexander Redington Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec
Environment:

OS X


Attachments: Text File CLJS-2387.patch    
Patch: Code and Test

 Description   

When Analyzing a cljs file which contains only (cljs.spec.alpha/def ...) forms and no clojure core (def ...) special forms, the Analyzer does not determine the source file to have been analyzed on https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/analyzer.cljc#L4021. When this is combined with the :cache-analysis true compiler option, it results in the analysis cache continually being repopulated from disk, and in certain degenerate cases, poor compilation performance.



 Comments   
Comment by Alexander Redington [ 19/Oct/17 1:30 PM ]

Steps to reproduce: have multiple cljs files require one namespace that contains only (s/def ...) forms, such that analysis is examined multiple times for the spec namespace. Run with compiler-options :verbose true and :cache-analysis true, and observe the file is analyzed multiple times and the cache is re-read multiple times.

Workarounds: Add a meaningless (def ...) form to the files which are repeatedly analyzed, and they will suddenly stop being analyzed more than once.

On my local machine doing this with the worst offendeding namespaces dropped compile times from 104 seconds to 34 seconds.

Comment by Mike Fikes [ 20/Nov/17 1:31 PM ]

There are a couple of places in the code that interpret the presence of defs in the analysis cache as implying that the namespace has been analyzed. (One is the spot pointed out by Alex, and the other is https://github.com/clojure/clojurescript/blob/2389e52049a9bd001d173a1cb4772ed8a25de196/src/main/clojure/cljs/analyzer.cljc#L2120 )

The attached patch essentially forces the issue, ensuring that there is at least an empty defs map upon successful analysis. (An alternative approach would be to revise the AST slightly by introducing an explicit :analyzed true key-value.)

Note that, in order to test with the attached patch, you'd need to ensure that the patch in CLJS-2405 is applied as well, as it affects proper cache loading, especially in the case where specs are involved.

Comment by Mike Fikes [ 24/Nov/17 6:35 PM ]

Another somewhat related issue to watch out for, even if this one is fixed: CLJS-2417

Comment by David Nolen [ 01/Dec/17 4:00 PM ]

fixed https://github.com/clojure/clojurescript/commit/9bcf0ff15247b37561d49e310cfb162bc0fc3156





[CLJS-2299] Failure with alias and bad require of clojure.spec Created: 04/Aug/17  Updated: 07/Aug/17  Resolved: 07/Aug/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.854
Fix Version/s: 1.9.908

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: regression, spec

Attachments: Text File CLJS-2299.patch    
Patch: Code
Approval: Accepted

 Description   

Try these two forms in succession with a QuickStart JAR node REPL:

(require '[clojure.spec :as s])
(require '[clojure.spec.alpha :as s])

The second will cause an error clojure.lang.ExceptionInfo: Alias s already exists in namespace cljs.user, aliasing clojure.spec

This does not occur with 1.9.671



 Comments   
Comment by António Nuno Monteiro [ 05/Aug/17 5:51 PM ]

Patch attached with fix.

Summary of the fix:

When we moved from the REPL special `require` last year to the new `:ns*` require, we stopped backing up the compiler state for requires and imports. The patch adds backing up of the compiler state and restores it if an analysis error occurs.

Comment by David Nolen [ 07/Aug/17 7:18 AM ]

fixed https://github.com/clojure/clojurescript/commit/870d8731014c3cfe56539580eefd5671437fde36





[CLJS-2197] Calling instrumented var fails to check conformance Created: 09/Jul/17  Updated: 02/Mar/18  Resolved: 22/Dec/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.671
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec


 Description   

If you spec a fn var and then instrument and call it, an args conformance check is made. If instead you call the var using the actual var in operator position, it will fail to make the conformance check. The same will occur with a HOF, as in applying the fn to an args sequence. These last two variants work in Clojure.

Repro:

$ java -jar cljs.jar -m cljs.repl.node
ClojureScript Node.js REPL server listening on 50246
To quit, type: :cljs/quit
cljs.user=> (require '[clojure.spec.alpha :as s] '[clojure.spec.test.alpha :as st])
true
cljs.user=>   (s/fdef clojure.core/symbol
    :args (s/alt :separate (s/cat :ns string? :n string?)
                 :str string?
                 :sym symbol?)
    :ret symbol?)
cljs.core/symbol
cljs.user=> (st/instrument)
[cljs.core/symbol]
cljs.user=> (symbol 3)
repl:13
throw e__5614__auto__;
^

Error: Call to #'cljs.core/symbol did not conform to spec:
In: [0] val: 3 fails at: [:args :separate :ns] predicate: string?
In: [0] val: 3 fails at: [:args :str] predicate: string?
In: [0] val: 3 fails at: [:args :sym] predicate: symbol?
:cljs.spec.alpha/spec  #object[cljs.spec.alpha.t_cljs$spec$alpha2801]
:cljs.spec.alpha/value  (3)
:cljs.spec.alpha/args  (3)
:cljs.spec.alpha/failure  :instrument

    at new cljs$core$ExceptionInfo (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:34869:10)
    at Function.cljs.core.ex_info.cljs$core$IFn$_invoke$arity$3 (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:34930:9)
    at Function.cljs.core.ex_info.cljs$core$IFn$_invoke$arity$2 (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:34926:26)
    at cljs$core$ex_info (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:34912:26)
    at /Users/mfikes/Downloads/.cljs_node_repl/cljs/spec/test/alpha.js:133:25
    at G__3555__delegate (/Users/mfikes/Downloads/.cljs_node_repl/cljs/spec/test/alpha.js:164:15)
    at G__3555 (/Users/mfikes/Downloads/.cljs_node_repl/cljs/spec/test/alpha.js:185:26)
    at repl:1:96
    at repl:9:3
    at repl:14:4
cljs.user=> (#'symbol 3)
repl:13
throw e__5614__auto__;
^

TypeError: name.indexOf is not a function
    at Function.cljs.core.symbol.cljs$core$IFn$_invoke$arity$1 (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:3531:16)
    at cljs.core.Var.G__8901__2 (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:3627:65)
    at cljs.core.Var.G__8901 [as call] (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:3773:19)
    at repl:1:2682
    at repl:9:3
    at repl:14:4
    at ContextifyScript.Script.runInThisContext (vm.js:44:33)
    at Object.runInThisContext (vm.js:116:38)
    at Domain.<anonymous> ([stdin]:50:34)
    at Domain.run (domain.js:242:14)
cljs.user=> (apply symbol [3])
repl:13
throw e__5614__auto__;
^

TypeError: name.indexOf is not a function
    at Function.cljs.core.symbol.cljs$core$IFn$_invoke$arity$1 (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:3531:16)
    at Object.cljs$core$apply_to [as apply_to] (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:12421:45)
    at Function.cljs.core.apply.cljs$core$IFn$_invoke$arity$2 (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:12860:18)
    at cljs$core$apply (/Users/mfikes/Downloads/.cljs_node_repl/.cljs_node_repl/cljs/core.js:12818:24)
    at repl:1:95
    at repl:9:3
    at repl:14:4
    at ContextifyScript.Script.runInThisContext (vm.js:44:33)
    at Object.runInThisContext (vm.js:116:38)
    at Domain.<anonymous> ([stdin]:50:34)


 Comments   
Comment by Mike Fikes [ 09/Jul/17 8:04 AM ]

It is worth noting that the second variant, (#'symbol 3) works in Planck (but not the third) (apply symbol [3]).

Comment by David Nolen [ 22/Dec/17 12:05 PM ]

This is a variant of direct invoke getting in the way of instrumentation, see CLJS-2397. This is because Var uses direct invokes in its IFn implementations.

Comment by David Nolen [ 22/Dec/17 2:10 PM ]

fixed https://github.com/clojure/clojurescript/commit/93a841b6e1a043e4bac0fcae3d82cc0410f7f3fc





[CLJS-2191] Clean up doc references to clojure.spec.* in favor of cljs.spec.* Created: 08/Jul/17  Updated: 12/Jul/17  Resolved: 09/Jul/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.671
Fix Version/s: 1.9.854

Type: Enhancement Priority: Critical
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: docstring, spec

Attachments: Text File CLJS-2191.patch    
Patch: Code
Approval: Accepted

 Description   

There are a handful of docstrings that refer to the namespaces as clojure.spec.* instead of cljs.spec.* that could be cleaned up:

  1. In the regex? predicate
  2. The keyword in the conform docstring
  3. The fdef docstring

These can also be cleaned up to append alpha (as is done in other docstrings).



 Comments   
Comment by David Nolen [ 09/Jul/17 8:13 AM ]

fixed https://github.com/clojure/clojurescript/commit/0f2a03faeaf5ccdbddb3ca3f40532fb69eef942c





[CLJS-2142] Can't instrument a namespace containing constants Created: 30/Jun/17  Updated: 30/Jun/17  Resolved: 30/Jun/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.562
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2142.patch    
Patch: Code and Test
Approval: Screened

 Description   

If a namespace contains constant, the macroexpansion to set! will cause the analyzer to balk.

Note: This is unrelated to the recent ^:const Var inlining optimization—it is just the regular behavior of set!.

Repro:

$ java -jar cljs.jar -m cljs.repl.node
ClojureScript Node.js REPL server listening on 51477
To quit, type: :cljs/quit
cljs.user=> (ns foo.core)
nil
foo.core=> (def ^:const pi 3.14159)
#'foo.core/pi
foo.core=> (in-ns 'cljs.user)
nil
cljs.user=> (require '[clojure.spec.test.alpha :as st])
true
cljs.user=> (st/instrument 'foo.core)
clojure.lang.ExceptionInfo: Can't set! a constant at line 1 <cljs repl> {:file "<cljs repl>", :line 1, :column 1, :root-source-info {:source-type :fragment, :source-form (st/instrument (quote foo.core))}, :tag :cljs/analysis-error}
	at clojure.core$ex_info.invokeStatic(core.clj:4617)
	at cljs.analyzer$error.invokeStatic(analyzer.cljc:653)
	at cljs.analyzer$error.invoke(analyzer.cljc:653)
...


 Comments   
Comment by Mike Fikes [ 30/Jun/17 12:57 PM ]

Proposed in #cljs-dev Slack that solution could be filtering such Vars as non-instrumentable, as opposed to turning off the set! guard internally just for this case, and David also had the same approach in mind.

Note: The attached patch employs a nil check on :const meta, just as is done by the analyzer for set!.

Comment by David Nolen [ 30/Jun/17 8:56 PM ]

fixed https://github.com/clojure/clojurescript/commit/d2c6966893d47772550bbc6842993310acb9e470





[CLJS-2119] s/form for s/& is qualified with `clojure.spec.alpha` Created: 24/Jun/17  Updated: 24/Jun/17  Resolved: 24/Jun/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.562
Fix Version/s: 1.9.655

Type: Defect Priority: Major
Reporter: Shogo Ohta Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2119.patch    
Patch: Code and Test
Approval: Accepted

 Description   
(s/form (s/& integer? even?))
;=> (clojure.spec.alpha/& #object[...] #object[...])

Expected to be qualified with cljs.spec.alpha (not with clojure.spec.alpha) like other specs:

(s/form (s/cat :int integer? :str string?))
;=> (cljs.spec.alpha/cat :int cljs.core/integer? :str cljs.core/string?)
(s/form (s/coll-of integer?))
;=> (cljs.spec.alpha/coll-of cljs.core/integer?)


 Comments   
Comment by David Nolen [ 24/Jun/17 11:00 AM ]

fixed https://github.com/clojure/clojurescript/commit/c7cf897f4fd84c6b1879708c4aeb3dc96f9207bd





[CLJS-2118] caching support for cljs.spec.test Created: 23/Jun/17  Updated: 23/Jun/17  Resolved: 23/Jun/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.562
Fix Version/s: 1.9.655

Type: Defect Priority: Major
Reporter: David Nolen Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Approval: Accepted

 Comments   
Comment by David Nolen [ 23/Jun/17 7:39 PM ]

fixed https://github.com/clojure/clojurescript/commit/a6ff1a729fb27ceb5c5beef684a8b03d1961b9b3





[CLJS-2052] Port new spec.alpha enhancements Created: 26/May/17  Updated: 26/May/17  Resolved: 26/May/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: 1.9.655

Type: Enhancement Priority: Major
Reporter: António Nuno Monteiro Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-2052.patch    
Patch: Code and Test

 Description   

Port tickets CLJ-2057,CLJ-2059, CLJ-2061, CLJ-2063, CLJ-2076, CLJ-2085 and CLJ-2153 that just landed in spec.alpha



 Comments   
Comment by David Nolen [ 26/May/17 1:11 PM ]

fixed https://github.com/clojure/clojurescript/commit/314dd98467dbdb13515e4dc1e28e3845edf0f430





[CLJS-1989] s/fdef expansion side effect fails when load cached source Created: 28/Mar/17  Updated: 23/Jun/17  Resolved: 23/Jun/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.562
Fix Version/s: 1.9.655

Type: Defect Priority: Major
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Approval: Accepted

 Description   

s/fdef has an expansion-time side effect that causes it to not operate properly if cached code is loaded.

To repro, have src/foo/core.cljs:

(ns foo.core
 (:require [clojure.spec.alpha :as s]))

(defn bar [x])

(s/fdef bar :args (s/cat :x number?))

Then:

$ java -cp cljs.jar:src clojure.main -m cljs.repl.nashorn
To quit, type: :cljs/quit
cljs.user=> (require '[clojure.spec.test.alpha :as st] 'foo.core)
true
cljs.user=> (st/instrument)
[foo.core/bar]
cljs.user=> :cljs/quit

Now this has been cached. If you exit and try again, (st/instrument) fails:

$ java -cp cljs.jar:src clojure.main -m cljs.repl.nashorn
To quit, type: :cljs/quit
cljs.user=> (require '[clojure.spec.test.alpha :as st] 'foo.core)
true
cljs.user=> (st/instrument)
[]


 Comments   
Comment by Thomas Heller [ 28/Mar/17 11:34 AM ]

s/def is also affected. It uses the cljs.spec/registry-ref atom which has a CLJ side (in cljs/spec.cljc) as well as a CLJS side (in cljs/spec.cljs). The CLJ is not populated when using the cache.

Comment by David Nolen [ 23/Jun/17 3:47 PM ]

fixed https://github.com/clojure/clojurescript/commit/f119f6596cb136ddcb4d35e76156124a331a4e8b





[CLJS-1980] port CLJ-2100 (s/nilable form should retain original spec form) Created: 14/Mar/17  Updated: 17/Mar/17  Resolved: 17/Mar/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: António Nuno Monteiro Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-1980.patch    
Patch: Code

 Comments   
Comment by David Nolen [ 17/Mar/17 1:47 PM ]

fixed https://github.com/clojure/clojurescript/commit/1c9802043de28b8c331bc7e3c27b651450147731





[CLJS-1979] Port CLJ-2043 (fix s/form of s/conformer) Created: 14/Mar/17  Updated: 17/Mar/17  Resolved: 17/Mar/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: 1.9.655

Type: Defect Priority: Major
Reporter: António Nuno Monteiro Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-1979.patch    
Patch: Code

 Description   

this is in clojure 1.9.0-alpha15



 Comments   
Comment by David Nolen [ 17/Mar/17 1:50 PM ]

fixed https://github.com/clojure/clojurescript/commit/6f4b313e4f9523e9e8345f13c4a55bb1b6f93ebc





[CLJS-1978] Port CLJ-2035 Created: 14/Mar/17  Updated: 17/Mar/17  Resolved: 17/Mar/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: 1.9.655

Type: Defect Priority: Minor
Reporter: António Nuno Monteiro Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-1978.patch    
Patch: Code and Test

 Description   

CLJ-2035 has made it into Clojure 1.9.0-alpha15



 Comments   
Comment by António Nuno Monteiro [ 14/Mar/17 12:40 PM ]

Attached patch with fix and tests.

Comment by David Nolen [ 17/Mar/17 1:54 PM ]

fixed https://github.com/clojure/clojurescript/commit/e6abaa746e815f9f040484f0647b2173bfc45592





[CLJS-1947] cljs.spec "and" passes :cljs.spec/invalid in next spec after failed if there are 4+ sub-specs Created: 19/Feb/17  Updated: 20/Feb/17  Resolved: 20/Feb/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.293
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Vlad Protsenko Assignee: Unassigned
Resolution: Not Reproducible Votes: 0
Labels: bug, clojurescript, spec


 Description   

This bug affects version 1.9.473, but I could not select it in select box, and I did not test it on other versions.

;; This code will print :cljs.spec/invalid once:
(s/conform (s/and pos? println println println) 0)
:cljs.spec/invalid
=> :cljs.spec/invalid

;; This code does not print anything
(s/conform (s/and pos? println println) 0)
=> :cljs.spec/invalid


 Comments   
Comment by Mike Fikes [ 19/Feb/17 9:26 PM ]

Duplicate of CLJS-1935.

Confirmed via git bisect that https://github.com/clojure/clojurescript/commit/ff0b11c123bf46d9e0efff164a7327fb269d2262 fixed the issue reported in this ticket.





[CLJS-1813] bring clojure.core.specs from clojure Created: 06/Oct/16  Updated: 22/Dec/17  Resolved: 22/Dec/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.76
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Yehonathan Sharvit Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: spec


 Description   

In clojure there is clojure.core.specs namespace.
It has not yet be ported in clojurescript.






[CLJS-1792] Can't load clojure.spec.test when clojure.test.check is unavailable Created: 23/Sep/16  Updated: 16/Jun/17  Resolved: 16/Jun/17

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Arne Brasseur Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: spec
Environment:
Unable to find source-code formatter for language: clojure. Available languages are: javascript, sql, xhtml, actionscript, none, html, xml, java
[org.clojure/clojure "1.9.0-alpha12"]
[org.clojure/clojurescript "1.9.229" :scope "provided"]


 Description   

Requiring clojure.spec.test results in an error, because it's looking for clojure.test.check.

(ns foo.bar
  (:require [clojure.spec.test]))
Caused by: clojure.lang.ExceptionInfo: No such namespace: clojure.test.check, could not locate clojure/test/check.cljs, clojure/test/check.cljc, or Closure namespace "clojure.test.check" in file file:/home/arne/.m2/repository/org/clojure/clojurescript/1.9.229/clojurescript-1.9.229.jar!/cljs/spec/test.cljs {:tag :cljs/analysis-error}

This problem goes away when adding org.clojure/test.check as a dependency.

This is not an issue in Clojure. An exception is only raised when calling a function that relies on test.check.



 Comments   
Comment by David Nolen [ 30/Sep/16 11:41 AM ]

This is not a bug per se, we can't do what Clojure does here. How to best handle is something to consider. Present a good idea and submit a patch.

Comment by Andrea Richiardi [ 24/Jan/17 4:46 PM ]

I went through cljs.spec.test and I have noticed that basically the [clojure.test.check :as stc] dependencies is there only for using the namespace for stc/ret and others. We could get rid of it and use the ns explicitely.

There is another require, which is [clojure.test.check.properties]. My guess is that it is there because of some side-effect (maybe it registers some spec?). I need some enlightenment here but I could work on a cut-off

Comment by David Nolen [ 16/Jun/17 12:52 PM ]

I don't really think this is a problem.





[CLJS-1760] Self-host: test-cljs-1757 failing in test-self-parity Created: 19/Aug/16  Updated: 19/Aug/16  Resolved: 19/Aug/16

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: 1.9.293

Type: Defect Priority: Minor
Reporter: Mike Fikes Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: bootstrap, spec

Attachments: Text File CLJS-1760.patch    
Patch: Code

 Description   
$ script/test-self-parity
Testing with Node
WARNING: baz is a single segment namespace at line 1
WARNING: Use of undeclared Var cljs.spec$macros/gen at line 77

Testing cljs.core-test

Testing cljs.reader-test

Testing clojure.string-test

Testing clojure.data-test

Testing cljs.letfn-test

Testing cljs.reducers-test

Testing cljs.binding-test

Testing cljs.macro-test

Testing cljs.top-level

Testing cljs.ns-test.foo

Testing foo.ns-shadow-test

Testing cljs.import-test

Testing cljs.spec-test

ERROR in (test-cljs-1757) (TypeError:NaN:NaN)
expected: (s/exercise-fn (quote cljs.spec-test/cljs-1757-x))
  actual: #object[TypeError TypeError: Cannot read property 'call' of undefined]

Testing cljs.clojure-alias-test

Ran 215 tests containing 17428 assertions.
0 failures, 1 errors.


 Comments   
Comment by António Nuno Monteiro [ 19/Aug/16 10:25 AM ]

Attached path with fix.

Comment by Mike Fikes [ 19/Aug/16 10:39 AM ]

LGTM. It is the “correct” fix; I missed when putting together CLJS-1720. All tests pass for me with António's patch.

Comment by David Nolen [ 19/Aug/16 10:42 AM ]

fixed https://github.com/clojure/clojurescript/commit/86a83d720beb44deb5d55d7d9c0bc2d5174816a3





[CLJS-1757] cljs.spec/exercise-fn doesn't work when passed a quoted symbol Created: 17/Aug/16  Updated: 17/Aug/16  Resolved: 17/Aug/16

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: 1.9.293

Type: Defect Priority: Major
Reporter: António Nuno Monteiro Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: spec

Attachments: Text File CLJS-1757.patch    
Patch: Code and Test

 Description   
cljs.user=> (require '[clojure.spec :as s])
nil
cljs.user=> (defn a [b] 2)
#'cljs.user/a
cljs.user=> (s/fdef a :args (s/cat ::first number?) :ret #(= % 2))
cljs.user/a
cljs.user=> (s/exercise-fn `a)
clojure.lang.ExceptionInfo: Assert failed: (symbol? sym) at line 1 <cljs repl> {:file "<cljs repl>", :line 1, :column 1, :root-source-info {:source-type :fragment, :source-form (s/exercise-fn (quote cljs.user/a))}, :tag :cljs/analysis-error}


 Comments   
Comment by António Nuno Monteiro [ 17/Aug/16 12:31 PM ]

Attached patch with fix and test.

Comment by David Nolen [ 17/Aug/16 7:32 PM ]

fixed https://github.com/clojure/clojurescript/commit/74db88011dd6dd64f55521f074f0315231be0e12





[CLJS-1754] Add boolean? generator Created: 16/Aug/16  Updated: 16/Jun/17  Resolved: 17/Aug/16

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: 1.9.293

Type: Defect Priority: Major
Reporter: Matt Burbidge Assignee: David Nolen
Resolution: Completed Votes: 0
Labels: cljs, spec

Attachments: Text File CLJS-1754.patch    
Patch: Code and Test

 Description   

In clojure I can do
`(gen/generate (s/gen int?)) ;; => 1094`
or
`(gen/generate (s/gen boolean?)) ;; => false`.

In clojurescript I can do
`(gen/generate (s/gen int?)) ;; => -308`.
But in clojurescript I can't do
`(gen/generate (s/gen boolean?)) ;; => Error: Unable to construct gen at: [] for: function cljs$core$boolean_QMARK_{return (x === true) || (x === false);}].`

As far as I can tell it's because there is no boolean generator.



 Comments   
Comment by António Nuno Monteiro [ 17/Aug/16 10:04 AM ]

Added patch with fix and test.

Note that running self parity tests now depends on CLJS-1756 being in place.

Comment by David Nolen [ 17/Aug/16 10:40 AM ]

fixed https://github.com/clojure/clojurescript/commit/2a7454837244cf7de3dfed1e48f46f86c33a1809





[CLJS-1752] stest/instrument never throws or :ret and :fn Created: 16/Aug/16  Updated: 16/Aug/16  Resolved: 16/Aug/16

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.293
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Jan Hein Hoogstad Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: cljs, spec


 Description   

When I replace the spec for the following function:

(defn create [query]
  (with-meta (map->Query query) {:spec ::specs/query}))

from:

(spec/fdef create
           :args (spec/cat :query ::specs/query)
           :ret ::specs/query
           :fn #(spec/valid? ::specs/meta (-> %1 :ret :meta)))

(stest/instrument `create)

to

(spec/fdef create
           :args (spec/cat :query ::specs/query)
           :ret string?
           :fn #(spec/valid? ::specs/meta (-> %1 :ret :meta)))

(stest/instrument `create)

it does not throw. As a matter of fact, I didn't get it to throw on any value that I give the :ret and :fn function. Removing the record with a plain map did not make a difference...



 Comments   
Comment by David Nolen [ 16/Aug/16 10:24 AM ]

This is by design.

Comment by Jan Hein Hoogstad [ 16/Aug/16 10:34 AM ]

Thanks for the quick reply, but it is not clear to me why? Can you maybe point me to an explanation?

Cheer!

Comment by Jan Hein Hoogstad [ 16/Aug/16 11:06 AM ]

Never mind, found it. Hadn't noticed it before...





[CLJS-1713] cljs.spec/explain-data returns different data structure than clojure.spec/explain-data Created: 24/Jul/16  Updated: 10/Aug/16  Resolved: 10/Aug/16

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Marshall Abrams Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: spec
Environment:

Tested in Clojure 1.9.0-alpha10 and Clojurescript 1.9.89



 Description   

The structure of the data returned by cljs.spec/explain-data seems to be unnecessarily different from the structure of data returned by clojure.spec/explain-data. Since, as I understand it, explain.data is mean to be used programmatically, this means that code has to be changed, unnecessarily, it seems, to work with both dialects.

Ignoring trivial differences that are to be expected going from one dialect to the other, in Clojure, the value of the top-level key :problems is a sequence of maps, each of which includes a :path key and value. In Clojurescript, the value of :problems is a map in which those vectors that were the values of :path in Clojure are instead keys, whose values are the rest of the corresponding Clojure maps.

Example:

(s/def ::a #(> % 0))
(s/def ::b (s/or :lt0 #(< % 0.0) :gt1 #(> % 1.0)))
(s/def ::all (s/keys :req-un [::a ::b]))
(s/explain ::all {:a -1 :b 0.5})
(pprint (s/explain-data ::all {:a -1 :b 0.5}))

In Clojure 1.9.0-alpha10, the last line returns:

#:clojure.spec{:problems ({:path [:a], :pred (> % 0), :val -1, :via [:user/all :user/a], :in [:a]}
                          {:path [:b :lt0], :pred (< % 0.0), :val 0.5, :via [:user/all :user/b], :in [:b]}
                          {:path [:b :gt1], :pred (> % 1.0), :val 0.5, :via [:user/all :user/b], :in [:b]})}

In Clojurescript 1.9.89, the same line returns:

{:cljs.spec/problems {[:a] {:pred (> % 0), :val -1, :via [:cljs.user/all :cljs.user/a], :in [:a]}, 
                      [:b :lt0] {:pred (< % 0), :val 0.5, :via [:cljs.user/all :cljs.user/b], :in [:b]},
                      [:b :gt1] {:pred (> % 1), :val 0.5, :via [:cljs.user/all :cljs.user/b], :in [:b]}}}


 Comments   
Comment by Marshall Abrams [ 24/Jul/16 11:58 AM ]

I just realized that Clojurescript 1.9.89 is still based on Clojure 1.9.0-alpha7, in which the data structure is like in the Clojurescript last example above. That explains the difference in the examples, and I assume that the explain-data return values will eventually be harmonized between the dialects, with future releases of Clojurescript (possibly after further changes in Clojure's explain-data return values, since this seems to be in flux). Apologies if this is just a "noise" ticket that shouldn't have been opened.

Comment by David Nolen [ 10/Aug/16 2:18 PM ]

fixed in master





[CLJS-1710] spec/double-in not implemented Created: 20/Jul/16  Updated: 23/Sep/16  Resolved: 23/Sep/16

Status: Closed
Project: ClojureScript
Component/s: None
Affects Version/s: 1.9.76
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Marshall Abrams Assignee: David Nolen
Resolution: Completed Votes: 1
Labels: cljs, newbie, spec
Environment:

Clojurescript 1.9.89



 Description   

spec/double-in is available in Clojure 1.9.0-alpha10, but doesn't seem to be implemented yet in Clojurescript as of 1.9.89. I also tried 1.9.76: not there either.

cljs.user=> (require '[cljs.spec :as s])
nil
cljs.user=> (s/valid? #(and (>= % 0.0) (<= % 1.0)) 1.0)
----  Compiler Warning on   <cljs form>   line:1  column:12  ----

  Use of undeclared Var cljs.spec/double-in

  1  (s/valid? (s/double-in :min 0.0 :max 1.0) 1.0)
                ^---

----  Compiler Warning  ----
#object[TypeError TypeError: Cannot read property 'call' of undefined]
nil

(Newbie ticket. Apologies if this is a dupe ticket or doesn't belong here. I couldn't find any tickets that seemed to mention this issue.)



 Comments   
Comment by David Nolen [ 23/Sep/16 2:22 PM ]

fixed https://github.com/clojure/clojurescript/commit/0ed939815738488efc2b627b8eeb3061a3c4dcd8





[CLJ-2503] [spec] non blank string spec Created: 11/Apr/19  Updated: 11/Apr/19

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Feature Priority: Trivial
Reporter: JAre Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: generator, predicate, spec, string

Approval: Triaged

 Description   

I often need a spec that can validate non blank string:

(s/def ::non-blank-string (complement str/blank?))

(s/valid? ::non-blank-string "foo")
;; => true

It throws with non string values and lacks a generator:

(s/exercise ::non-blank-string)
;; =>  Unhandled clojure.lang.ExceptionInfo...

(s/valid? ::non-blank-string 42)
;; => Unhandled java.lang.ClassCastException...

It can be solved with:

(s/def ::non-blank-string (s/and string? (complement str/blank?)))

(s/exercise ::non-blank-string)
;; => (["8" "8"] ["pS" "pS"] ["RL" "RL"] ["3" "3"] ["c2ZUx" "c2ZUx"] ["24VF" "24VF"] ["0wc80" "0wc80"] ["Wr49vz" "Wr49vz"] ["1UDte" "1UDte"] ["2f" "2f"])

(s/valid? ::non-blank-string 42)
;; => false

The problem: The need in this spec seems to be recurring problem and I end up re-implementing it again and again. Sometimes I wrongly name it ::non-empty-string or create a separate namespace with the spec so I can reuse it.

Solution: Make it standard predicate. Mb it can be called text?






[CLJ-2498] [spec] force a function to validate it's input using fdef definitions Created: 27/Mar/19  Updated: 27/Mar/19

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Feature Priority: Minor
Reporter: Tommi Reiman Assignee: Unassigned
Resolution: Unresolved Votes: 2
Labels: spec


 Description   

Currently, there is no way to force function fdef validation on, outside of tests and immune to override with dynamic vars. Forcing a validation in a function can be done using either with a) :pre hook or b) a manual s/conform / s/valid? call but those do not contribute to the function documentation. There are many use-cases for functions that always validate the inputs, e.g. when configuring components on application startup, done once where the performance penalty doesn't matter and correctness is important.

Schema has the :always-validate metadata for this case:

(require '[schema.core :as s])

(s/defn ^:always-validate interceptor-x
  [opts :- {:string? s/Bool}])

(interceptor-x {})
; Syntax error (ExceptionInfo) compiling at (test.cljc:150:1).
; Input to interceptor-x does not match schema:
;
;       [(named {:string? missing-required-key} opts)]


 Comments   
Comment by Tommi Reiman [ 27/Mar/19 9:50 AM ]

Maybe something like:

(require '[clojure.spec.alpha :as s])

(defn plus1 [x]
  (inc x))

(s/fdef ^:always-validate plus1
        :args (s/cat :x int?))

(plus1 "1")
; Syntax error (ExceptionInfo) compiling at (user.clj:18:1).
; Call to #'user/plus1 did not conform to spec.




[CLJ-2496] [spec] explain-data :via loses aliased keys Created: 24/Mar/19  Updated: 24/Mar/19

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Krzysztof Władyka Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: spec


 Description   

Full code example:

(ns api.foo
  (:require [clojure.spec.alpha :as s]))

(s/def ::common-email (s/and string? (partial re-matches #"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,63}$")))

;(s/def :foo/email (s/and ::common-email))
(s/def :foo/email ::common-email)

(s/def :db/foo (s/keys :req [:foo/email]))

(comment
  (->> (s/explain-data :db/foo {:foo/email "bar"})
       ::s/problems))

Issue:

({:path [:foo/email],
  :pred (clojure.core/partial clojure.core/re-matches #"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,63}$"),
  :val "bar",
  :via [:db/foo :api.foo/common-email],
  :in [:foo/email]})

Dirty hack
But if I use `(s/def :foo/email (s/and ::common-email))` instead it return

({:path [:foo/email],
  :pred (clojure.core/partial clojure.core/re-matches #"^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,63}$"),
  :val "bar",
  :via [:db/foo :foo/email :api.foo/common-email],
  :in [:foo/email]})

expected behaviour
(s/def :foo/email ::common-email)
return
{{:via [:db/foo :foo/email :api.foo/common-email]}}

What happen here?
[:db/foo :api.foo/common-email] vs [:db/foo :foo/email :api.foo/common-email]

So (s/def :foo/email ::common-email) is totally omit here. In my opinion it is a bug, not a feature

Why is it important to fix?
It is important to keep it full tracked to turn this errors to right communication in User Interface.
So `[:db/foo :foo/email :api.foo/common-email]`, I am looking if I have proper message for UI. First `:api.foo/common-email`. No message, then check `:foo/email`. I have message for it so I can return "E-mail is not valid".

In practice it can be `:user/password` with split spec for length, special characters etc. validation or `:company/vat-id` based on country. But always I don't want to keep final validation at that moment, because things like street, phone number, vat-id, email etc. are common and I want to have one definition in one place.

On top of it I can do for example `(s/def :user/email (s/and (s/conformer clojure.string/lower-case) ::s-common/email))`. Here is the point. But not all e-mail validations will do lower-case. So today I have to use dirty hack or have redundant e-mail validation in all places which is harder to maintenance.

I don't want to base on `::common-email`, because this part is considered to change in any moment. It can be bookkeeping library with common definition for European Union vat-id. I don't want to base messages for UI for this library, I want to base on my definitions in my code, but `(s/def :company/vat-id ::bookkeeping-library/vat-id)` lose in `:via`.

We can imagine it is deeper and more complex structure. At that moment figuring out what is the issue of fail is rocket science, that is why I use dirty hack `(s/def :foo/email (s/and ::common-email))` to read it simple from `:via`.






[CLJ-2486] [spec] the generator for "inst?" generate inst's that can be readed after print Created: 19/Feb/19  Updated: 19/Feb/19  Resolved: 19/Feb/19

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Enzzo Cavallo Assignee: Unassigned
Resolution: Duplicate Votes: 0
Labels: generator, print, reader, spec
Environment:

org.clojure/clojure {:mvn/version "1.10.0"}
org.clojure/test.check {:mvn/version "0.10.0-alpha3"}}



 Description   

Here:
https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/gen/alpha.clj#L160

inst? generator is defined as (fmap #(java.util.Date. %) (large-integer))

But inst's generated from integer's larges then 253402300799999 can be readed

(edn/read-string (pr-str (new java.util.Date (inc 253402300799999)))) ;; throws: Unrecognized date/time syntax: 10000-01-01T00:00:00.000-00:00

One line reproduce:
clj -Srepro \
-Sdeps '{:deps {org.clojure/clojure {:mvn/version "1.10.0"} org.clojure/test.check {:mvn/version "0.10.0-alpha3"}}}' \
-e "(do (require '[clojure.test.check.generators :as gen] '[clojure.spec.alpha :as s]) (gen/generate (gen/vector (s/gen inst?)) 120 6))"

[#inst "1970-01-01T01:24:38.462-00:00" #inst "1969-12-06T11:54:24.666-00:00" #inst "820499-07-18T05:28:23.398-00:00" #inst "1969-08-08T01:04:53.430-00:00" #inst "1970-01-01T00:00:00.000-00:00" #inst "1970-01-01T00:00:00.001-00:00"]

the 3 el, if printed, can be readed.

If the solution for this is "allow reader to read huge dates", please consider transit/cljs patches.

The actual limits of reader are:
[(new Date -62135769600000) (new Date 253402300799999)]
Use (gen/large-integer* {:min -62135769600000 :max 253402300799999}) is a possible patch, but the actual gen generate just 1970+ (positive) dates.



 Comments   
Comment by David Bürgin [ 19/Feb/19 4:18 PM ]

same as CLJ-2347

Comment by Alex Miller [ 19/Feb/19 4:41 PM ]

ah, sorry I didn't catch, will close as a dupe





[CLJ-2482] graalvm native-image compatibility Created: 14/Feb/19  Updated: 14/Feb/19  Resolved: 14/Feb/19

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Defect Priority: Major
Reporter: JAre Assignee: Unassigned
Resolution: Duplicate Votes: 0
Labels: spec
Environment:

docker


Attachments: Text File err.log    

 Description   

I have a command line tool that benefits greatly from the ability to be compiled into a native executable. It works fine with Clojure 1.9 but doesn't compile with Clojure 1.10

I narrowed down the issue and made a reproduction project:
broken (Clojure 1.10) build log
working (Clojure 1.9) build log
diff

Error log:

Error: unbalanced monitors: mismatch at monitorexit, 96|LoadField#lockee_5436auto_ != 3|LoadField#lockee_5436auto_
Detailed message:
Error: unbalanced monitors: mismatch at monitorexit, 96|LoadField#lockee_5436auto_ != 3|LoadField#lockee_5436auto_
Call path from entry point to clojure.spec.gen.alpha$dynaload$fn__2628.invoke():
at clojure.spec.gen.alpha$dynaload$fn__2628.invoke(alpha.clj:21)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.AFn.applyTo(AFn.java:144)
at clj10_graal_repro.core.main(Unknown Source)
at com.oracle.svm.core.JavaMainWrapper.run(JavaMainWrapper.java:152)
at com.oracle.svm.core.code.IsolateEnterStub.JavaMainWrapper_run_5087f5482cc9a6abc971913ece43acb471d2631b(generated:0)
Original exception that caused the problem: org.graalvm.compiler.code.SourceStackTraceBailoutException$1: unbalanced monitors: mismatch at monitorexit, 96|LoadField#lockee_5436auto_ != 3|LoadField#lockee_5436auto_
at clojure.spec.gen.alpha$dynaload$fn__2628.invoke(alpha.clj:22)
Caused by: org.graalvm.compiler.core.common.PermanentBailoutException: unbalanced monitors: mismatch at monitorexit, 96|LoadField#lockee_5436auto_ != 3|LoadField#lockee_5436auto_
at org.graalvm.compiler.java.BytecodeParser.bailout(BytecodeParser.java:3673)
at org.graalvm.compiler.java.BytecodeParser.genMonitorExit(BytecodeParser.java:2654)
at org.graalvm.compiler.java.BytecodeParser.processBytecode(BytecodeParser.java:5031)
at org.graalvm.compiler.java.BytecodeParser.iterateBytecodesForBlock(BytecodeParser.java:3184)
at org.graalvm.compiler.java.BytecodeParser.processBlock(BytecodeParser.java:2993)
at org.graalvm.compiler.java.BytecodeParser.build(BytecodeParser.java:890)
at org.graalvm.compiler.java.BytecodeParser.buildRootMethod(BytecodeParser.java:784)
at org.graalvm.compiler.java.GraphBuilderPhase$Instance.run(GraphBuilderPhase.java:95)
at org.graalvm.compiler.phases.Phase.run(Phase.java:49)
at org.graalvm.compiler.phases.BasePhase.apply(BasePhase.java:197)
at org.graalvm.compiler.phases.Phase.apply(Phase.java:42)
at org.graalvm.compiler.phases.Phase.apply(Phase.java:38)
at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.parse(MethodTypeFlowBuilder.java:205)
at com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder.apply(MethodTypeFlowBuilder.java:324)
at com.oracle.graal.pointsto.flow.MethodTypeFlow.doParse(MethodTypeFlow.java:310)
at com.oracle.graal.pointsto.flow.MethodTypeFlow.ensureParsed(MethodTypeFlow.java:300)
at com.oracle.graal.pointsto.flow.MethodTypeFlow.addContext(MethodTypeFlow.java:107)
at com.oracle.graal.pointsto.flow.SpecialInvokeTypeFlow.onObservedUpdate(InvokeTypeFlow.java:421)
at com.oracle.graal.pointsto.flow.TypeFlow.notifyObservers(TypeFlow.java:347)
at com.oracle.graal.pointsto.flow.TypeFlow.update(TypeFlow.java:389)
at com.oracle.graal.pointsto.flow.SourceTypeFlowBase.update(SourceTypeFlowBase.java:121)
at com.oracle.graal.pointsto.BigBang$2.run(BigBang.java:508)
at com.oracle.graal.pointsto.util.CompletionExecutor.lambda$execute$0(CompletionExecutor.java:174)
at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1402)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Error: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception
Error: Image building with exit status 1

Error log with-H:+ReportExceptionStackTraces in attachment.



 Comments   
Comment by Alex Miller [ 14/Feb/19 8:47 PM ]

This is a dupe of the issue in CLJ-1472 if you want to add info there.

Comment by JAre [ 14/Feb/19 8:48 PM ]

Apparently Docker hub logs aren't public anymore. At least not failed one. So I'll link them here:

Clojure 1.9 log
Clojure 1.10 log

Comment by JAre [ 14/Feb/19 8:51 PM ]

OK. Thx.





[CLJ-2481] [spec] every-impl breaks when passed records Created: 13/Feb/19  Updated: 13/Feb/19

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Sean Harrap Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec
Environment:

Mac OS X 10.13.6


Approval: Triaged

 Description   

Specs relying on every-impl (such as every, coll-of, etc) may break when passed a record. For example:

REPL Session
> (require ['clojure.spec.alpha :as 's])

> (s/def ::coll-any (s/coll-of any?))
> (defrecord Pair [first second])

> (s/valid? ::coll-any 3)
false

> (s/valid? ::coll-any {:a 1 :b 2}
true

> (s/valid? ::coll-any (Pair. 1 2))
Execution error (UnsupportedOperationException) at user.Pair/empty (REPL:1).
Can't create empty: user.Pair





[CLJ-2472] [spec] 'check' silently ignores symbols which are not checkable Created: 30/Jan/19  Updated: 31/Jan/19

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: Josip Gracin Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: error-reporting, spec
Environment:

[org.clojure/spec.alpha "0.2.176"], [org.clojure/core.specs.alpha "0.2.44"]


Attachments: Text File clj-2472-1.patch     Text File clj-2472-2.patch    
Approval: Triaged

 Description   

Function clojure.spec.test.alpha/check silently ignores symbols which are not "checkable". This seems counter-intuitive to me because I'd expect it to fail when I call check on a symbol and the symbol does not have a spec (e.g. it is misspelled).

The definition of check looks like this:

(defn check
 ([] (check (checkable-syms)))
 ([sym-or-syms] (check sym-or-syms nil))
 ([sym-or-syms opts]
    (->> (collectionize sym-or-syms)
         (filter (checkable-syms opts))
         (pmap
          #(check-1 (sym->check-map %) opts)))))

It seems to me that the (filter (checkable-syms opts)) might better go to the no-arg variant.



 Comments   
Comment by Josip Gracin [ 30/Jan/19 9:43 AM ]

Attaching clj-2472-1.patch. It preserves backward-compatibility which complicates it a bit.

Comment by Josip Gracin [ 31/Jan/19 3:24 AM ]

Minor change to the patch (using 'when' instead of 'if'). The idea of the solution is to introduce a new option :assert-checkable which asserts that provided syms are checkable.





[CLJ-2468] [spec] NPE when using Var with spec/conform Created: 16/Jan/19  Updated: 16/Jan/19  Resolved: 16/Jan/19

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Greg Chapman Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: spec

Attachments: Text File fn-sym-NPE-2.patch     Text File fn-sym-NPE.patch    
Patch: Code

 Description   

Example:

user=> (declare myint?)
#'user/myint?
user=> (defn myconformer [thing] (s/conform #'myint? thing))
#'user/myconformer
user=> (defn myint? [thing] (int? thing))
#'user/myint?
user=> (myconformer 1)
Execution error (NullPointerException) at java.util.regex.Matcher/getTextLength (Matcher.java:1770).

Looking at the code, I believe using a Var in this way will end up invoking the Object implementation for
specize* . Since a Var is an ifn?, fn-sym will be called with the Var as the argument. However, fn-sym
appears to expect only something which is an fn? – the regex match will fail, binding nil to f-ns and f-n,
and then demunge will cause the NPE.

I suggest the test in specize* for Object be changed to fn? (see attached patch).



 Comments   
Comment by Greg Chapman [ 16/Jan/19 5:53 PM ]

It occurred to me later that, if fn? is the correct test, we don't have to screen out maps. So here is a patch which only checks fn? before calling fn-sym.

Comment by Alex Miller [ 16/Jan/19 9:57 PM ]

In the next version of spec, specize doesn't exist anymore and a var is not going to be a valid thing to pass to conform (I'd say it's only marginally so in spec 1, certainly outside what was envisioned). So since it's not going to be valid in the future, I'm going to decline this ticket.





[CLJ-2460] Importing java class through collection fails silently Created: 28/Dec/18  Updated: 28/Dec/18  Resolved: 28/Dec/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Sanel Zukan Assignee: Unassigned
Resolution: Not Reproducible Votes: 0
Labels: compiler, spec


 Description   

When java class is imported in current namespace using vector/list syntax, but without package list format, import fails silently and class is not imported at the end. Here is example:

user=> (import java.util.Calendar)
#<Class@799f10e1 java.util.Calendar>
user=> (import [java.text.SimpleDateFormat])
nil
user=> SimpleDateFormat
   ...
   java.lang.RuntimeException: Unable to resolve symbol: SimpleDateFormat in this context
   ...
user=> (import [java.text SimpleDateFormat])
user=> #<Class@3ed03652 java.text.SimpleDateFormat>

This behavior is very confusing, especially if is done in code and compiler will not complain about it.



 Comments   
Comment by Alex Miller [ 28/Dec/18 9:08 AM ]

The spec was tightened to catch this in Clojure 1.10 and this now yields an error:

user=> (import [java.text.SimpleDateFormat])
Syntax error macroexpanding clojure.core/import at (REPL:1:1).
java.text.SimpleDateFormat - failed: #{(quote quote)} at: [:class :quoted-spec :quote] spec: :clojure.core.specs.alpha/quotable-import-list
() - failed: Insufficient input at: [:package-list :spec :classes] spec: :clojure.core.specs.alpha/package-list
java.text.SimpleDateFormat - failed: #{(quote quote)} at: [:package-list :quoted-spec :quote] spec: :clojure.core.specs.alpha/quotable-import-list
[java.text.SimpleDateFormat] - failed: simple-symbol? at: [:class :spec] spec: :clojure.core.specs.alpha/quotable-import-list
Comment by Sanel Zukan [ 28/Dec/18 9:24 AM ]

Thanks for quick response. Didn't know this was fixed in 1.10, which I'm currently not running.





[CLJ-2450] [spec] defining new multimethod for a speced multi method fails under instrumentation Created: 06/Dec/18  Updated: 08/Jan/19

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Erik Assum Assignee: Unassigned
Resolution: Unresolved Votes: 2
Labels: spec

Approval: Triaged

 Description   
23:11 $ clj -Sdeps '{:deps {org.clojure/clojure {:mvn/version "1.10.0-RC3"} org.clojure/test.check {:mvn/version "0.10.0-alpha2"}}}'
Clojure 1.10.0-RC3
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (require '[clojure.spec.test.alpha :as st])
nil
user=> (defmulti foo identity)
#'user/foo
user=> (s/fdef foo :args (s/cat :wat string?))
user/foo
user=> (defmethod foo :bar [lol])
#object[clojure.lang.MultiFn 0x57ac5227 "clojure.lang.MultiFn@57ac5227"]
user=> (st/instrument)
[user/foo]
user=> (defmethod foo :baz [lol])
Execution error (ClassCastException) at user/eval150 (REPL:1).
clojure.spec.test.alpha$spec_checking_fn$fn__3026 cannot be cast to clojure.lang.MultiFn
user=> *e
#error {
 :cause "clojure.spec.test.alpha$spec_checking_fn$fn__3026 cannot be cast to clojure.lang.MultiFn"
 :via
 [{:type java.lang.ClassCastException
   :message "clojure.spec.test.alpha$spec_checking_fn$fn__3026 cannot be cast to clojure.lang.MultiFn"
   :at [user$eval150 invokeStatic "NO_SOURCE_FILE" 1]}]
 :trace
 [[user$eval150 invokeStatic "NO_SOURCE_FILE" 1]
  [user$eval150 invoke "NO_SOURCE_FILE" 1]
  [clojure.lang.Compiler eval "Compiler.java" 7176]
  [clojure.lang.Compiler eval "Compiler.java" 7131]
  [clojure.core$eval invokeStatic "core.clj" 3214]
  [clojure.core$eval invoke "core.clj" 3210]
  [clojure.main$repl$read_eval_print__9068$fn__9071 invoke "main.clj" 414]
  [clojure.main$repl$read_eval_print__9068 invoke "main.clj" 414]
  [clojure.main$repl$fn__9077 invoke "main.clj" 435]
  [clojure.main$repl invokeStatic "main.clj" 435]
  [clojure.main$repl_opt invokeStatic "main.clj" 499]
  [clojure.main$main invokeStatic "main.clj" 598]
  [clojure.main$main doInvoke "main.clj" 561]
  [clojure.lang.RestFn invoke "RestFn.java" 397]
  [clojure.lang.AFn applyToHelper "AFn.java" 152]
  [clojure.lang.RestFn applyTo "RestFn.java" 132]
  [clojure.lang.Var applyTo "Var.java" 705]
  [clojure.main main "main.java" 37]]}
user=>

I would expect this to not throw an exception



 Comments   
Comment by Dennis Schridde [ 08/Jan/19 10:24 AM ]

For reference and in case someone else would also like to know the call order that works (with Clojure 1.10.0 and clojure.spec.alpha 0.2.176):

Unable to find source-code formatter for language: clj. Available languages are: javascript, sql, xhtml, actionscript, none, html, xml, java
(require '[clojure.spec.alpha :as s])
; => nil
(require '[clojure.spec.test.alpha :as stest])
; => nil
(defmulti testfn :type)
; => #'user/testfn
(defmethod testfn :atype [m] 1)
; => #object[clojure.lang.MultiFn 0x5d16b612 "clojure.lang.MultiFn@5d16b612"]
(s/fdef testfn :args (s/cat :m (s/keys :req-un [::type ::does-not-exist])))
; => user/testfn
(stest/instrument `testfn)
; => [user/testfn]
(testfn {:type :atype :does-not-exist nil})
; => 1
(testfn {:type :atype})
; => clojure.lang.ExceptionInfo: Call to #'user/testfn did not conform to spec.




[CLJ-2443] [spec] Spec'ed fn doesn't throw when called lazily Created: 23/Nov/18  Updated: 26/Nov/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Michiel Borkent Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec

Attachments: Text File CLJ-2443.patch     Text File CLJ-2443-test.patch    
Patch: Code and Test
Approval: Triaged

 Description   

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.



 Comments   
Comment by Michiel Borkent [ 24/Nov/18 9:50 AM ]

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

Comment by Michiel Borkent [ 25/Nov/18 7:27 AM ]

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

Comment by Michiel Borkent [ 25/Nov/18 7:29 AM ]

Updated description with CLJ-2443-test.patch





[CLJ-2440] [core.specs] spec for fn does not allow anonymous function name Created: 20/Nov/18  Updated: 21/Nov/18  Resolved: 20/Nov/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9, Release 1.10
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Alan Thompson Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: spec, test
Environment:

Ubuntu 16.04
Java 10


Attachments: File jira.clj    

 Description   

In the following macro code fragment, the function ID `fixture-fn` causes the anonymous function to fail clojure.spec:

`(ct/use-fixtures ~mode
   (fn fixture-fn [tgt-fn#]
     (~enter-fn ~ctx)
     (tgt-fn#)
     (~leave-fn ~ctx))))

When function name fixture-fn is absent, compilation proceeds and clojure.spec works fine. When function name is present,
the error message is:

Caused by: clojure.lang.ExceptionInfo: Call to clojure.core/fn did not conform to spec. 

  {:clojure.spec.alpha/problems ({:path [:fn-tail :arity-1 :params], :pred clojure.core/vector?, :val tupelo.test/fixture-fn, 
                                  :via  [:clojure.core.specs.alpha/params+body
                                         :clojure.core.specs.alpha/param-list
                                         :clojure.core.specs.alpha/param-list], :in [0]}
                                 {:path [:fn-tail :arity-n], 
                                  :pred (clojure.core/fn [%] (clojure.core/or 
                                                               (clojure.core/nil? %)
                                                               (clojure.core/sequential? %))),
                                  :val tupelo.test/fixture-fn,
                                  :via [:clojure.core.specs.alpha/params+body
                                        :clojure.core.specs.alpha/params+body], :in [0]}),
  :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__2509 0x21b8e988 "clojure.spec.alpha$regex_spec_impl$reify__2509@21b8e988"] ,
  :clojure.spec.alpha/value (tupelo.test/fixture-fn [tgt-fn__16599__auto__]
                              ((fn [ctx] (println "*** TEST ONCE *** - enter ")) {:line 25, :column 1})
                              (tgt-fn__16599__auto__)
                              ((fn [ctx] (println "*** TEST ONCE *** - leave ")) {:line 25, :column 1})),
  :clojure.spec.alpha/args (tupelo.test/fixture-fn [tgt-fn__16599__auto__]
                              ((fn [ctx] (println "*** TEST ONCE *** - enter ")) {:line 25, :column 1})
                              (tgt-fn__16599__auto__)
                              ((fn [ctx] (println "*** TEST ONCE *** - leave ")) {:line 25, :column 1}))}


 Comments   
Comment by Alan Thompson [ 20/Nov/18 6:40 PM ]

Darn, JIRA destroyed all my nice formatting.... See attached file `jira.clj` for formatted source code and error message

Comment by Alex Miller [ 20/Nov/18 8:24 PM ]

Fixed the formatting for you

Comment by Alex Miller [ 20/Nov/18 8:27 PM ]

The problem here I think is that the syntax quote will fully resolve fixture-fn and the spec requires an unqualified symbol in a function name. You can easily fix that by preventing resolution but leaving the symbol:

`(ct/use-fixtures ~mode
   (fn ~'fixture-fn [tgt-fn#]
     (~enter-fn ~ctx)
     (tgt-fn#)
     (~leave-fn ~ctx))))

Would appreciate if you could try that and see if it works.

Comment by Alex Miller [ 20/Nov/18 8:30 PM ]

I'm about 99% sure this is the issue. A minimal repro would be something like:

(fn a/b [])

which should just be:

(fn b [])
Comment by Alex Miller [ 20/Nov/18 8:31 PM ]

Going to decline this as I believe the issue is in the code, not the spec. If it's not this, please re-open.

Comment by Alan Thompson [ 21/Nov/18 1:11 AM ]

You are correct. Using

~'fixture-fn

solved the issue.





[CLJ-2418] [spec] (s/exercise string?) produces narrow string samples Created: 20/Oct/18  Updated: 20/Oct/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: JAre Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: generative-test, generator, spec, string

Approval: Triaged

 Description   

(s/exercise string?) seems to never produce strings with delimiters or spaces. I noticed this while testing my string formatting function with generative testing - it was extremely surprising.

It looks like a potential source of low quality tests and bad surprises.






[CLJ-2411] [spec] need additional parameter `comment` on 's/assert' Created: 04/Oct/18  Updated: 04/Oct/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Enhancement Priority: Trivial
Reporter: eunpyoung kim Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: spec

Patch: Code
Approval: Triaged

 Description   

currently spec doesn't have comment parameter for detail.'

https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/alpha.clj#L1970

```
(defmacro assert
[spec x]
```

I think it is better to add comment parameter for debugging.

```
(defmacro assert
([spec x])
([spec x comment]))
```






[CLJ-2410] [spec] When spec is simple alias for another spec, :via paths contain duplicates Created: 03/Oct/18  Updated: 04/Oct/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Ben Brinckerhoff Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: error-reporting, spec
Environment:

org.clojure/spec.alpha {:mvn/version "0.2.176"}


Approval: Triaged

 Description   

Aliasing specs is a useful practice. For instance, a common ':myproj/name' spec may be used for two different keys e.g. ':myproj.user/name' and ':myproj.admin/name'.

However, when these are used in a spec, the ':via' path will include duplicates e.g. include ':myproj/name' twice.

Repro:

1. clj -Srepro -Sdeps '{:deps {org.clojure/spec.alpha {:mvn/version "0.2.176"}}}'

(require '[clojure.spec.alpha :as s])
(s/def :myproj/name (s/and string? #(< 2 (count %))))
(s/def :myproj.user/name :myproj/name)
(s/def :myproj/user-args (s/cat :name :myproj.user/name))
(-> (s/explain-data :myproj/user-args [""]) ::s/problems pprint)

[{:path [:name],
  :pred (clojure.core/fn [%] (clojure.core/< 2 (clojure.core/count %))),
  :val "",
  :via [:myproj/user-args :myproj/user-args :myproj/name :myproj/name],
  :in [0]}]

Actual: the ':via' path for the problem is '[:myproj/user-args :myproj/user-args :myproj/name :myproj/name]'

Expected: I would have expected to see the spec ':myproj.user/name' in the ':via', since the ':myproj/user-args' spec is built in terms of ':myproj.user/name'.






[CLJ-2406] [spec] `cat` and `keys*` have different semantics for `:in` path when spec fails Created: 25/Sep/18  Updated: 04/Oct/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Ben Brinckerhoff Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: error-reporting, spec
Environment:

org.clojure/spec.alpha {:mvn/version "0.2.176"}


Approval: Triaged

 Description   

When a spec fails a 'keys*' spec, the 'in' path refers to the path after conforming. This is different from other specs (including 'cat' specs), where the 'in' path indicates the path in the original (pre-conformed) data.

rlwrap clj -Srepro -Sdeps '{:deps {org.clojure/spec.alpha {:mvn/version "0.2.176"}}}'
Clojure 1.9.0
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/def ::point1 (s/cat :x int? :y int?))
user/point1
user=> (s/def ::x int?)
:user/x
user=> (s/def ::y int?)
:user/y
user=> (s/def ::point2 (s/keys* :req-un [::x ::y]))
:user/point2
user=> ;; for `cat` spec, `in` will refer to the position before conformance
user=> (s/explain ::point1 [0 nil])
nil - failed: int? in: [1] at: [:y] spec: :user/point1
nil
user=> ;; but for `keys*` spec, `in` will refer to position AFTER conformance
user=> (s/explain ::point2 [:x 0 :y nil])
nil - failed: int? in: [:y] at: [:y] spec: :user/y
nil
user=>

This is confusing for users looking at the default error message, since their original data doesn't have a ':y' key in the example. Furthermore, this makes it difficult for third-party libs to display the bad value in context.






[CLJ-2398] [spec] s/* macro no longer coerces hash-maps to seq of k-v vector pairs Created: 30/Aug/18  Updated: 31/Aug/18  Resolved: 31/Aug/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Scott Lowe Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: spec
Environment:

Clojure 1.10.0-alpha6, Java 1.8.0_162, macOS 10.14



 Description   

It's not clear to me if the following change in behaviour of clojure.spec.alpha/* between Clojure 1.9 & 1.10 is by design.

;; clojure "1.9.0"

(s/explain (s/* vector?)
           {2 5})

;; Success!
;; => nil

(s/conform (s/* vector?)
           {2 5})

;; => [[2 5]]


;; ------------------------------------------------------


;; clojure "1.10.0-alpha6"

(s/explain (s/* vector?)
           {2 5})

;; val: {2 5} fails predicate: (or (nil? %) (sequential? %))
;; => nil

(s/conform (s/* vector?)
           {2 5})
           
=> :clojure.spec.alpha/invalid

The previous behaviour was useful because it was possible to use calls to s/cat in a flexible way:

(s/conform (s/* (s/spec (s/cat :k even? :v odd?)))
           {2 5
            4 7})

;; => [{:k 2, :v 5} {:k 4, :v 7}]
(s/conform (s/* (s/cat :k even? :v odd?))
           [2 5
            4 7])

;; => [{:k 2, :v 5} {:k 4, :v 7}]

I can see why it might be argued that this is further tightening up of Spec; but would like to know if this is the case, before I change my client code to match the new behaviour.



 Comments   
Comment by Alex Miller [ 31/Aug/18 6:00 AM ]

Yes, this is an intentional change - regex ops are inherently about describing sequential structure. Applying them to an unordered data structure like a map or set is a recipe for eventual unhappiness. Tightening this up allowed some other cases to be handled better.

Your second example is illustrative - while this will work with literal maps (which are array maps which retain order), there is no guarantee that a general unordered hash map will present the entries in the order you’re expecting.

If you really want this behavior, you can explicitly seq your map before conforming. (Could even be done in the spec with s/conformed).





[CLJ-2396] [spec] Omit "in:" spec problem info when reporting if all indexes Created: 28/Aug/18  Updated: 05/Sep/18  Resolved: 05/Sep/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: Release 1.10

Type: Enhancement Priority: Major
Reporter: Alex Miller Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: error-reporting, spec

Attachments: Text File clj-2396-2.patch     Text File clj-2396-3.patch     Text File clj-2396.patch    
Patch: Code
Approval: Ok

 Description   

When reporting spec problems, function arg problems are almost always positional in nature (syntax), as defined in a regex spec. In those cases, reporting the "in:" data paths are often more confusing than useful.

For example:

user=> (let [a/b 1] b)
CompilerException clojure.lang.ExceptionInfo: Call to clojure.core/let did not conform to spec:
In: [0 0] val: a/b fails spec: :clojure.core.specs.alpha/local-name at: [:args :bindings :binding :sym] predicate: simple-symbol?
In: [0 0] val: a/b fails spec: :clojure.core.specs.alpha/seq-binding-form at: [:args :bindings :binding :seq] predicate: vector?
In: [0 0] val: a/b fails spec: :clojure.core.specs.alpha/map-bindings at: [:args :bindings :binding :map] predicate: coll?
In: [0 0] val: a/b fails spec: :clojure.core.specs.alpha/map-special-binding at: [:args :bindings :binding :map] predicate: map?

Here, "In: [0 0]" is not helping me as a user understand the issue (although the data is helpful to error printers like expound). However, if these are nested s/keys specs, the in: data are keywords which provide a lot of information.

Proposed: When printing in the repl, if the explain printer is the default explain printer, remove all of the :in data from the explain-data problems before printing. (But don't do this if a custom printer is being used, like expound.)

user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/explain int? :a)
val: :a fails predicate: int?
nil
user=> (s/explain (s/cat :i int?) [:a])
val: :a fails at: [:i] predicate: int?
nil
user=> (s/explain (s/map-of keyword? int?) {"hi" 5})
In: ["hi" 0] val: "hi" fails at: [0] predicate: keyword?
nil

Patch: clj-2396-3.patch






[CLJ-2393] [spec] Improve sorting on problem printing Created: 28/Aug/18  Updated: 04/Sep/18  Resolved: 04/Sep/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: Release 1.10

Type: Enhancement Priority: Major
Reporter: Alex Miller Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: error-reporting, spec

Attachments: Text File clj-2393.patch    
Patch: Code
Approval: Ok

 Description   

Currently, problems are sorted based on the length of the :path, but in cases of a tied path, it would be helpful to also sort on :in (with longer ones first, indicating you got "farther" in the data).

Example before (other patches also applied here, so doesn't exactly match 1.9):

user=> (let [[a :as b c] [1 2 3]] a)  ;; c is invalid here
Syntax error macroexpanding clojure.core/let at (1:1). Cause: Call to clojure.core/let did not conform to spec.
val: [a :as b c] failed pred: simple-symbol? in: [0 0] at: [:bindings :form :local-symbol] spec: :clojure.core.specs.alpha/local-name
val: (c) failed: Extra input in: [0 0 3] at: [:bindings :form :seq-destructure] spec: :clojure.core.specs.alpha/seq-binding-form
val: [a :as b c] failed pred: map? in: [0 0] at: [:bindings :form :map-destructure] spec: :clojure.core.specs.alpha/map-bindings
val: [a :as b c] failed pred: map? in: [0 0] at: [:bindings :form :map-destructure] spec: :clojure.core.specs.alpha/map-special-binding

Here the "at" paths are all the same length but the "in" length is important and the longer one is most interesting (as it got the furthest).

Proposed: Sort first on "in" length, before the existing sort on "at".

Example after:

user=> (let [[a :as b c] [1 2 3]] a)
Syntax error macroexpanding clojure.core/let at (1:1). Cause: Call to clojure.core/let did not conform to spec.
val: (c) failed: Extra input in: [0 0 3] at: [:bindings :form :seq-destructure] spec: :clojure.core.specs.alpha/seq-binding-form
val: [a :as b c] failed pred: simple-symbol? in: [0 0] at: [:bindings :form :local-symbol] spec: :clojure.core.specs.alpha/local-name
val: [a :as b c] failed pred: map? in: [0 0] at: [:bindings :form :map-destructure] spec: :clojure.core.specs.alpha/map-bindings
val: [a :as b c] failed pred: map? in: [0 0] at: [:bindings :form :map-destructure] spec: :clojure.core.specs.alpha/map-special-binding

Patch: clj-2393.patch



 Comments   
Comment by Alex Miller [ 04/Sep/18 7:17 PM ]

Applied





[CLJ-2392] [spec] fspec role in problem path is not useful Created: 27/Aug/18  Updated: 04/Sep/18  Resolved: 04/Sep/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: Release 1.10

Type: Enhancement Priority: Major
Reporter: Alex Miller Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: error-reporting, spec

Attachments: Text File clj-2392.patch    
Patch: Code
Approval: Ok

 Description   

Currently, spec function errors prepend the function "role" (really always :args) which creates a false first segment that is always there.

user=> (let [a/b 1] b)
CompilerException clojure.lang.ExceptionInfo: Call to clojure.core/let did not conform to spec:
In: [0 0] val: a/b fails spec: :clojure.core.specs.alpha/local-name at: [:args :bindings :binding :sym] predicate: simple-symbol?
In: [0 0] val: a/b fails spec: :clojure.core.specs.alpha/seq-binding-form at: [:args :bindings :binding :seq] predicate: vector?
In: [0 0] val: a/b fails spec: :clojure.core.specs.alpha/map-bindings at: [:args :bindings :binding :map] predicate: coll?
In: [0 0] val: a/b fails spec: :clojure.core.specs.alpha/map-special-binding at: [:args :bindings :binding :map] predicate: map?

Note the "at: [:args ...]" - the :args here is manually inserted by spec problem reporting. It occurs on the path of every problem line report and thus does not add any information.

Proposed: Remove the explicit "role" path.

Patch: clj-2392.patch



 Comments   
Comment by Alex Miller [ 04/Sep/18 7:20 PM ]

Applied





[CLJ-2391] [spec] Printing of spec problems buries the failing predicate which should be more prominent Created: 22/Aug/18  Updated: 04/Sep/18  Resolved: 04/Sep/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: Release 1.10

Type: Enhancement Priority: Major
Reporter: Alex Miller Assignee: Unassigned
Resolution: Completed Votes: 0
Labels: error-reporting, spec

Attachments: Text File clj-2391-2.patch     Text File clj-2391-3.patch     Text File clj-2391.patch    
Patch: Code
Approval: Ok

 Description   

After CLJ-2376, you might see an error like this:

user=> (let [a #_1 b 2])
CompilerException clojure.lang.ExceptionInfo: Call to clojure.core/let did not conform to spec:
In: [0] val: [a b 2] fails spec: :clojure.core.specs.alpha/bindings at: [:args :bindings] predicate: even-number-of-forms?

This puts the spec (long, not initially relevant) before the predicate which is more important.

1.9 spec problem line format:

[In IN]val: VAL fails[ spec: SPEC][ at:PATH] predicate: PRED[, REASON]

Proposed: Put value and pred first and if there is an internal "reason" that should take precedence.

Proposed format:

VAL failed: [REASON|PRED][ in: IN][ at: PATH][ spec: SPEC]

Example afterwards:

user=> (let [a #_1 b 2])
[a b 2] failed: even-number-of-forms? in: [0] at: [:args :bindings] spec: :clojure.core.specs.alpha/bindings

Patch: clj-2391-3.patch



 Comments   
Comment by Alex Miller [ 28/Aug/18 6:52 PM ]

Added -2 patch that removes the leading "val:"

Comment by Alex Miller [ 04/Sep/18 7:14 PM ]

Applied





[CLJ-2390] [spec] Customize printer for clojure.spec.test/check Created: 20/Aug/18  Updated: 20/Aug/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Feature Priority: Major
Reporter: Andrea Richiardi Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: error-reporting, spec, test

Approval: Triaged

 Description   

At the moment, the machinery that produces spec error messages as a result of a check call does not seem to follow the same (successful) pattern of s/explain: there is no way to set explain-out.

It would be awesome to either use that or have a separate dynamic var for it so that tools like expound can hook into that.






[CLJ-2389] [spec] Expose size parameter in clojure.spec.gen.alpha/generate Created: 17/Aug/18  Updated: 17/Aug/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Enhancement Priority: Minor
Reporter: David Bürgin Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: generator, spec
Environment:

Clojure 1.10.0-alpha6
clojure.spec.alpha 0.2.168


Approval: Triaged

 Description   

The lazy-loaded clojure.spec.gen.alpha/generate function only exposes one parameter of its delegate clojure.test.check.generators/generate:

(generate generator)

It should perhaps also accept the size parameter of clojure.test.check.generators/generate:

(generate generator size)

The default size 30 easily leads to OutOfMemoryError with complex specs.






[CLJ-2388] [spec] s/merge causes failure to roundtrip conform then unform Created: 16/Aug/18  Updated: 16/Aug/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Shlomi Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec
Environment:

linux


Approval: Triaged

 Description   

Opened the bug after discussion in this thread: https://groups.google.com/forum/#!topic/clojure/r8WO24rHsi0

Essentially, when using s/merge with s/or, s/conform returns a structure that cannot be s/unform'ed.

Steps to reproduce (shamelessly copied from the thread):

(require '[clojure.spec.alpha :as s])
(s/def ::a (s/or :even even? :odd odd?))
(s/def ::b (s/or :even even? :odd odd?))
(s/def ::m1 (s/keys :req-un [::a]))
(s/def ::m2 (s/keys :req-un [::b]))
(s/def ::mm (s/merge ::m1 ::m2))
(s/valid? ::mm {:a 1 :b 2})      ;; true
(s/conform ::mm {:a 1 :b 2})
;;=> {:a 1, :b [:even 2]}
(s/unform ::mm {:a [:odd 1] :b [:even 2]})
;;=> {:a 1, :b [:even 2]}
(s/unform ::mm (s/conform ::mm {:a 1 :b 2}))





[CLJ-2381] [spec] ::defn-args spec does not capture type hint on return value Created: 06/Aug/18  Updated: 07/Aug/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Mark Engelberg Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec
Environment:

Windows, Java 1.8


Approval: Triaged

 Description   

Conforming a defn to the ::defn-args spec correctly preserves any type hint given to the inputs, but incorrectly loses the type hint given to the return value.

user=> (require '[clojure.spec.alpha :as s] '[clojure.core.specs.alpha :as ss])
nil
user=> (def c '(f ^double [^long x] 0.0))
#'user/c
user=> (binding [*print-meta* true] (prn c))
^{:line 10, :column 9} (f ^double [^long x] 0.0)
nil
user=> (binding [*print-meta* true] (prn (s/conform ::ss/defn-args c)))
{:name f, :bs [:arity-1 {:args {:args [[:sym ^long x]]}, :body [:body [0.0]]}]}
nil

Cause: Conformed regex specs do not retain meta on their conformed value (coll specs do):

user=> (binding [*print-meta* true] (prn (s/conform (s/coll-of int?) ^{:hi :there} [1])))
^{:hi :there} [1]

user=> (binding [*print-meta* true] (prn (s/conform (s/* int?) ^{:hi :there} [1])))
[1]

The particular spec where this comes up here is :clojure.core.specs.alpha/arg-list, which is an s/*.






[CLJ-2378] [spec] instrument should check :ret and :fn specs Created: 16/Jul/18  Updated: 17/Jul/18  Resolved: 16/Jul/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Feature Priority: Major
Reporter: jcr Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: spec


 Description   

Problem: a function instrumented via clojure.spec.test.alpha/instrument checks the :args part of its spec, but not the :ret and :fn parts. It's confusing and forces people to use a 3rd-party library[1].

Solution: add an option to turn on :ret and :fn checking to clojure.spec.test.alpha/instrument.

[1]: https://github.com/jeaye/orchestra



 Comments   
Comment by Alex Miller [ 16/Jul/18 5:59 PM ]

The basic idea here is that instrument is designed to verify that a function is invoked correctly. For determining whether the internals of a function are correct, you can use `check`.

This has been discussed at great length, both on the core team and in the broader community and we’re familiar with all the arguments. While I’m not ruling out the possibility that we will do something in this area in the future, we are not currently planning on changing this, so I’m going to decline this for now. I’ll repeat, saying no to this now does not prevent us from saying yes later.

Comment by Ghadi Shayban [ 16/Jul/18 6:04 PM ]

https://dev.clojure.org/display/community/Creating+Tickets
https://groups.google.com/forum/#!topic/clojure/RLQBFJ0vGG4/discussion
There's also some commentary from Rich in Slack, but I don't have a link handy.
("we should do X" tickets are almost universally declined.)
dup of https://dev.clojure.org/jira/browse/CLJ-1961

Comment by jcr [ 17/Jul/18 10:44 AM ]

Sorry, I've only searched through open issues before creating this ticket.

Is it at all possible to keep this issue open so people could vote for it and subscribe to it, since (as Alex stated) it's not set in stone yet?


(The thing is, I was confused by this behavior and asked on irc and elsewhere, and was told by several people that it's weird and looks like a bug. I've filed it as a "feature" since the doc clearly states that's the intended behavior. Also, it's not quite clear how to use check to verify correctness of a function for a specific subset of possible inputs (think checking the fn in repl as you develop it) - maybe the spec guide could elaborate a bit more on what the supposed workflow should look like? Maybe that'd clear up some confusion; though, I understand that's out of the scope.)

Comment by Alex Miller [ 17/Jul/18 12:14 PM ]

As I said, we're aware of the request and why people want it. From a core team perspective, we don't need an issue to track it.

Use `check` if you want to use property based testing to verify the correctness of a function with respect to its spec. Generally if you want to test specific possible inputs, most people use example-based tests with clojure.test instead - that's not in the guide as it doesn't really have anything to do with spec. It is possible to override the args generator with a set spec of explicit inputs, but I'm not sure if the effort of that is worth the work most of the time.

Comment by jcr [ 17/Jul/18 12:57 PM ]

Well, fair enough!

Re example-based tests: what I had in mind is 1) calling a function (presumably in repl) with a specific input to see if the output still conforms to :ret and :fn specs; 2) testing a function A which calls spec'ed functions B1..Bn to see which of those fail their :ret/:fn specs (essentially it gives you free unit tests for each integration test). I agree that it's theoretically possible to do that in vanilla clojure.spec with a custom generator, but that certainly doesn't worth the effort.

Anyway, I guess you've already heard this argument, so I'll stop there.





[CLJ-2377] [spec] StackOverflowError in gen of recursive spec Created: 15/Jul/18  Updated: 15/Jul/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Defect Priority: Major
Reporter: David Bürgin Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: generator, spec
Environment:

Clojure 1.10.0-alpha6


Approval: Triaged

 Description   

Repro stripped down from larger piece of code:

clj -Srepro -Sdeps '{:deps {org.clojure/clojure {:mvn/version "1.10.0-alpha6"}, org.clojure/test.check {:mvn/version "0.10.0-alpha3"}}}'
(require '[clojure.spec.alpha :as s])

(s/def ::m (s/keys :req [::coll]))
(s/def ::coll (s/cat :m (s/? ::m)))

(s/conform ::m {::coll []})  ; => #:user{:coll {}}
(s/exercise ::m)  ; => StackOverflowError

Unlike in CLJ-2002, conform works fine here, but generation recurs for ever.






[CLJ-2376] [core.specs] Check early if let binding vector is even Created: 13/Jul/18  Updated: 28/Aug/18  Resolved: 28/Aug/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: Release 1.10

Type: Enhancement Priority: Minor
Reporter: Alexander Yakushev Assignee: Unassigned
Resolution: Duplicate Votes: 1
Labels: spec

Attachments: Text File clj-2376-2.patch     Text File let-even.patch    
Patch: Code
Approval: Vetted

 Description   

If you miss a value in the let binding vector, Spec will return a rather verbose and misguiding message:

user=> (require '[clojure.spec.alpha :as s] '[clojure.core.specs.alpha :as cs])
user=> (s/explain ::cs/bindings '[a #_1 b 2])
In: [2] val: 2 fails spec: :clojure.core.specs.alpha/local-name at: [:binding :sym] predicate: simple-symbol?
In: [2] val: 2 fails spec: :clojure.core.specs.alpha/seq-binding-form at: [:binding :seq] predicate: vector?
In: [2] val: 2 fails spec: :clojure.core.specs.alpha/map-bindings at: [:binding :map] predicate: coll?
In: [2] val: 2 fails spec: :clojure.core.specs.alpha/map-special-binding at: [:binding :map] predicate: map?

The crux of the problem here is the odd number of parameters in the binding. Spec gets to it anyway, but if it first finds a parameter that is an invalid binding form, it will bark at that instead. With the suggested patch, the error looks like:

user=> (s/explain ::cs/bindings '[a #_1 b 2])
val: [a b 2] fails spec: :clojure.core.specs.alpha/bindings predicate: even-number-of-forms?

Patch: clj-2376-2.patch



 Comments   
Comment by Alex Miller [ 22/Aug/18 11:01 AM ]

Add -2 patch that uses predicate with more useful semantics

Comment by Alex Miller [ 28/Aug/18 9:48 AM ]

Subsumed into CLJ-2395





[CLJ-2373] improvements to exception messages and printing Created: 09/Jul/18  Updated: 05/Sep/18  Resolved: 05/Sep/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: None
Fix Version/s: Release 1.10

Type: Enhancement Priority: Critical
Reporter: Stuart Halloway Assignee: Unassigned
Resolution: Completed Votes: 21
Labels: error-reporting, errormsgs, spec

Attachments: Text File clj-2373-2.patch     Text File clj-2373-3.patch     Text File clj-2373-4.patch     Text File clj-2373-5.patch     Text File clj-2373-6.patch     Text File clj-2373-7.patch     Text File clj-2373-8.patch     Text File clj-2373-9.patch     Text File clj-2373-clojure-1.patch     Text File clj-2373-spec-alpha-1.patch     Text File clj-2373-spec-alpha-2.patch     Text File example-errors-1.9.txt     Text File example-errors.txt    
Patch: Code
Approval: Ok

 Description   

Problems

  • Discussions about "make errors better" often conflate error message, stacktraces, exception data
  • Errors do not clearly indicate in what "phase" of execution they occur (read, compile, macroexpand, evaluation, printing)
  • Errors during macroexpansion do not capture the location in caller source (like read and compile exceptions do), and thus the wrong "location" is reported
  • Clojure prints data into Throwable messages
    • big and ugly
    • outside user control

Principles

  • exception messages should be small strings
    • with the expectation that getMessage will be printed
    • totally controlled by throwing code
    • never print arbitrary data into a message
  • flow data all the way to the edge
    • ex-info & ex-data
    • macroexpand spec errors have a well known format
  • edge printing should be concise by default, configurable by user
    • edge functions like repl-caught respect print and spec bindings
    • concise summary by default
    • all the details when you want

Proposed impl changes

  • stop printing data into message strings
    • ExceptionInfo.toString should list keys, not entire map contents
    • spec.alpha/macroexpand-check should stop including explain-out in message
  • and instead print data (configurably) at the edges repl/pst and main/repl-caught
    • print spec per explain-out
    • print ExceptionInfo keys
  • make CompilerException wrappers special at print time instead of construct time
    • CE message is "CompilerException + file/line"
    • wrapped message is whatever it is
    • edge printers should print both

Discussion

  • what should tools do?
    • leverage the data provided in the exception chain to do whatever they want
    • can use main/repl-caught as a guide
  • what happens to programs that parse exception messages?
    • probably should not do this (Clojure's tests do this but try to minimize it)
    • this proposal changes some wrapper message text, but that was never part of the contract

Patch Status

  • clj-2373-spec-alpha-2.patch - for spec.alpha - don't print explain data into message (callers can do what they want with that)
  • clj-2373-9.patch - for clojure
    • LispReader - made ReaderException line/column fields public so CompilerEx can swipe them
    • Compiler
      • CompilerException now implements IExceptionInfo and works with ex-data
      • Created well-known keys :clojure.error/source,line,column,phase,symbol
      • Created well-known phases :read, :macroexpand, :compile
      • Left existing CompilerException fields, but stored rest into data map
      • Construct new syntax error messages on construction
      • Use toString() to encapsulate combining wrapper and cause into a message
      • On read, spec, macroexpand exceptions, add appropriate wrapping
      • Tweaked existing compiler exception calls as much as possible to record symbol (more could potentially be done here - in some cases we have forms or non-symbol things we could report)
      • Moves "Cause:" down to second line except for macroexpand spec errors
    • Var - expose a method to get the symbol for a var. Many people have requested this functionality via a core function, maybe this is a step in that direction.
    • clojure.main
      • add init-cause (private) function to get initial root (rather than existing weird root-cause)
      • add ex-str function (public) to construct the message to print based on an ex. tools could use this. Embeds some of the phase/printing logic but piggiebacks on CompilerException.toString() for some as well. Handles spec problem ex-data specially - this could be genericized but I have not tried to do that here.
      • change repl-caught to rely on ex-str
      • change main repl code to catch and wrap read and print exceptions to specialize errors
    • test* - in general these are tweaks to test to check the cause message rather than the wrapper message with the help of a new assertion type defined in test-helper

Also see: https://dev.clojure.org/display/design/Exception+handling+update

Screener's Notes

See example-errors.txt attachment for resulting error message examples.



 Comments   
Comment by Stuart Halloway [ 09/Jul/18 12:14 PM ]

Output from a REPL session with the "-1" patches: https://gist.github.com/stuarthalloway/d3055d1975b464e8949b854e77a746b1

Comment by jcr [ 09/Jul/18 8:46 PM ]

re "many Clojure tests that parse exception strings need to change":

Is it desirable to decouple the error message (as printed to the user) from the error tag/type/kind (for tooling, tests, etc), and make the latter a part of the contract?

Comment by Alexander Yakushev [ 10/Jul/18 1:49 AM ]

I get the eliding map values from the output is meant to hide the ginormous Spec explanations. But printing only the keys doesn't have much value (pun intended). Is there possibly another solution? I can see two suboptimal ones:

1) Dissoc only the :clojure.spec.alpha/ keywords from the ex-data, Spec being the main offender.
2) Print ex-data with small print-length and print-depth (not sure whether that achieves anything, have to analyze different Spec errors to see if it does).

Comment by Stuart Halloway [ 10/Jul/18 6:21 AM ]

Thanks for the suggestions!

jcr: That might be desirable, but it is an expansion of scope and is not necessary to solve this problem.

Alexander: This is not just about spec errors, but all ex-data. The data is already present and accessible, and printing the keys gives the user a hint where to look.

Comment by Alex Miller [ 10/Jul/18 7:52 AM ]

I have dug into the test failures more and there are really two things here...

1) the top level exception messages have changed (and are generally worse for the particular case of a CompilerException with nested init exception, which is not in the example set). I think this bears revisiting for those cases where you might be in a "Java calling Clojure" environment.

2) the message printed to the repl has generally improved, however most of the failing tests are checking exception message as a proxy for what the user sees in a particular error situation via thrown-with-msg?. It would be helpful if "what is printed" could be more easily extracted and if the tests were actually checking that, rather than checking the exception message - I think this better reflects the tests' intent.

I'm working on an updated patch.

Comment by Alex Miller [ 12/Jul/18 12:12 AM ]

clj-2373-2.patch makes fewer changes in the CompilerException message (tests all continue to pass) but does more in the printing of exception chains in the repl, ignoring the computed message of the wrapper CompilerException completely and focusing more attention on the initial cause (but adding back in the source location info in the CompilerException wrapper if it's available).

user=> (/ 1 0)
ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:163)

user=> (throw (ex-info "oops" {:a 1 :b "foo"}))
ExceptionInfo oops clojure.core/ex-info (core.clj:4754)
ex-data keys: [:a :b]

user=> (let [a])
ExceptionInfo Call to clojure.core/let did not conform to spec., compiling:(3:1) clojure.core/ex-info (core.clj:4754)
In: [0] val: () fails spec: :clojure.core.specs.alpha/bindings at: [:args :bindings :init-expr] predicate: any?,  Insufficient input
Comment by Alex Miller [ 12/Jul/18 11:04 PM ]

Dumped some further design work at https://dev.clojure.org/display/design/Exception+handling+update

Still a WIP, take nothing here as final.

Comment by Alex Miller [ 11/Aug/18 4:11 PM ]

Pushed -3 patch for current state (does not address test failures yet).

Comment by Phill Wolf [ 12/Aug/18 11:26 AM ]

A vote for principles of brevity + data-to-the-edge. At the point of throw, code has no idea how many GB of stuff it's including in ex-data and no basis on which to prune it.

Comment by Alex Miller [ 21/Aug/18 10:05 AM ]

Added -5 patch which removes the macroexpand sub-phase differences and isolates that into the message construction. Also cleans up the repl read exception wrapping to not confusingly wrap in CompilerException.

Comment by Alex Miller [ 22/Aug/18 9:11 AM ]

-6 patch fixes a couple bugs in printing read errors from source files.

Comment by Alex Miller [ 22/Aug/18 5:01 PM ]

Added -7 that fixes a double-period on error reading from source file.

Comment by Alex Miller [ 04/Sep/18 7:42 PM ]

Applied spec patch...

Comment by Alex Miller [ 04/Sep/18 9:05 PM ]

Added -9 patch that fixes test errors in -8 patch





[CLJ-2372] spec/valid? and spec/explain inconsistent for coll-of :kind Created: 09/Jul/18  Updated: 09/Jul/18  Resolved: 09/Jul/18

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Dale Thatcher Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: spec
Environment:

Windows XP, MacOS 10.13, JDK 1.8.0_171, Clojure 1.9.0, clojure.spec.alpha



 Description   

valid? and explain-str give inconsistent results when using coll-of :kind with a registered spec.

(s/def ::vector? vector?)
=> :user/vector?
(s/def ::vector-of-maps (s/coll-of map? :kind ::vector?))
=> :user/vector-of-maps
(s/valid? ::vector-of-maps [{}])
=> false
(s/explain-str ::vector-of-maps [{}])
=> "Success!\n"



 Comments   
Comment by Alex Miller [ 09/Jul/18 8:24 AM ]

It is not valid to use a spec for :kind.

Comment by Dale Thatcher [ 09/Jul/18 8:34 AM ]

Ok, the doc string doesn't match then:

coll-of refers you to "every" which has:

:kind - a pred/spec that the collection type must satisfy, e.g. vector?
(default nil) Note that if :kind is specified and :into is
not, this pred must generate in order for every to generate."

Also I noticed that the documentation for coll-of specifies:

Returns a spec for a collection of items satisfying pred.

But in this case a spec can be used.

Comment by Alex Miller [ 09/Jul/18 9:03 AM ]

The doc was fixed in the newest release of spec.alpha (0.2.168) - see CLJ-2111.

Comment by Dale Thatcher [ 09/Jul/18 9:57 AM ]

Understood, sorry to have wasted your time. I'll check the latest source next time, before raising a Jira.

Comment by Alex Miller [ 09/Jul/18 10:00 AM ]

No worries at all! That’s what triage is all about.





[CLJ-2371] [spec] alt with an empty cat breaks explain Created: 06/Jul/18  Updated: 08/Oct/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9, Release 1.10
Fix Version/s: Release 1.11

Type: Defect Priority: Major
Reporter: jcr Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec

Approval: Vetted

 Description   
(s/conform (s/alt :empty (s/cat)) [42]) ; => ::s/invalid
(s/explain (s/alt :empty (s/cat)) [42]) ; => prints 'Success!'

Related:
CLJ-2360 - looks like the same issue, but for s/and, s/or
CLJ-2336 - looks like a consequence of this issue
CLJ-2304 - probably related too






[CLJ-2368] [spec] describe* of spec-impl returns nil :args and :fn Created: 29/Jun/18  Updated: 08/Oct/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: Release 1.11

Type: Defect Priority: Minor
Reporter: Alexander Kiel Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec

Attachments: Text File clj-2368.patch    
Patch: Code
Approval: Vetted

 Description   

The form of an empty fspec returns nil at :args and :fn:

(s/form (s/fspec))
=> (clojure.spec.alpha/fspec :args nil :ret clojure.core/any? :fn nil)

The problem is, that the specs from CLJ-2112 don't validate that form. I suggest to implement describe* of fspec-impl like that of map-spec-impl which omits nil values.

Patch: clj-2368.patch



 Comments   
Comment by Alex Miller [ 29/Jun/18 8:55 AM ]

Sounds right.





[CLJ-2364] [spec] If value is invalid due to "Insufficient input", alternatives are not returned as separate problems Created: 25/Jun/18  Updated: 25/Jun/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Ben Brinckerhoff Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: spec


 Description   

For context, if I supply an invalid value for an 'alt' spec, explain-data will return two problems:

(s/explain-data (s/cat
                 :x (s/alt :i int? :s string?))
                [:kw])
;; explain-data contains two problems, one for each alternative:
;; #:clojure.spec.alpha{:problems ({:path [:x :i], :pred clojure.core/int?, :val :kw, :via [], :in [0]} {:path [:x :s], :pred clojure.core/string?, :val :kw, :via [], :in [0]}), :spec {:clojure.spec.alpha/op :clojure.spec.alpha/pcat, :ps [{:clojure.spec.alpha/op :clojure.spec.alpha/alt, :ps (#function[clojure.core/int?] #function[clojure.core/string?--5132]), :ks (:i :s), :forms (clojure.core/int? clojure.core/string?), :id #uuid "88448dbf-dce7-4789-9266-aa150a6563bc"}], :ret {}, :ks [:x], :forms [(clojure.spec.alpha/alt :i clojure.core/int? :s clojure.core/string?)], :rep+ nil}, :value [:kw]}

However, if the value is missing an element, explain-data returns a single problem

(s/explain-data (s/cat
                 :x (s/alt :i int? :s string?))
                [])
;; explain-data contains a single problem, with a compound predicate
;; #:clojure.spec.alpha{:problems [{:path [:x], :reason "Insufficient input", :pred (clojure.spec.alpha/alt :i clojure.core/int? :s clojure.core/string?), :val (), :via [], :in []}], :spec {:clojure.spec.alpha/op :clojure.spec.alpha/pcat, :ps [{:clojure.spec.alpha/op :clojure.spec.alpha/alt, :ps (#function[clojure.core/int?] #function[clojure.core/string?--5132]), :ks (:i :s), :forms (clojure.core/int? clojure.core/string?), :id #uuid "5f655d8c-19bb-43de-a153-722dd338d1f3"}], :ret {}, :ks [:x], :forms [(clojure.spec.alpha/alt :i clojure.core/int? :s clojure.core/string?)], :rep+ nil}, :value []}

Motivation: for specs for macros with many alternatives, the errors for a missing element are fairly opaque. For example, try

(defn hello "hello world")






[CLJ-2360] [spec] Inconsistent results from s/valid? and s/explain when using s/or without parameters Created: 19/Jun/18  Updated: 08/Oct/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: None
Fix Version/s: Release 1.11

Type: Defect Priority: Minor
Reporter: Björn Ebbinghaus Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: spec
Environment:

org.clojure/spec.alpha "0.1.143"


Attachments: Text File clj-2360.patch    
Patch: Code and Test
Approval: Vetted

 Description   

When using spec/or without parameters spec/valid? and spec/explain are resulting in a contradiction.

Minimal example:

(s/valid? (s/or) :something)
=> false
(s/explain (s/or) :something)
Success!
=> nil

Similar to or, s/or should fail to validate anything (so first call is good) but should report an error with explain (so explain result is bad).

Proposed: In the s/or explain, the logic maps over all preds to build a problem list about each value not matching, but the base case of no preds isn't explicitly handled. The change in the code checks for that case and builds a specific explain problem error for the overall s/or not matching. Patch includes fix and tests for (s/or) and (s/and) (which is ok).

Patch: clj-2360.patch






[CLJ-2348] [spec] 'check' has inconsistent behavior for required and optional map keys Created: 16/Apr/18  Updated: 15/Dec/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Ben Brinckerhoff Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec
Environment:

org.clojure/spec.alpha "0.1.143"


Approval: Triaged

 Description   

Repro:

(s/def :ex/f fn?)
(s/def :ex/m (s/keys :opt [:ex/f]))
(s/fdef my-fn
        :args (s/cat :m :ex/m))
(defn my-fn [f])
(clojure.spec.test.alpha/check `my-fn)

Actual: Exception is thrown - "Unable to construct gen at: [:m :ex/f] for: :ex/f"

Expected: A value should be returned containing the failure. This is the behavior that will occur if you replace the ":opt" with a ":req" in the keys spec.

I would expect this value to contain a failure such that:

(ex-data (:result (:clojure.spec.test.check/ret (first (clojure.spec.test.alpha/check `my-fn))))) ;; => #:clojure.spec.alpha{:path [:m :ex/f], :form :ex/f, :failure :no-gen}





[CLJ-2347] [spec] 'inst?' spec generator produces unreadable instants Created: 15/Apr/18  Updated: 20/Feb/19

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: David Bürgin Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: spec
Environment:

Clojure 1.9.0
org.clojure/spec.alpha 0.1.143
org.clojure/test.check 0.9.0


Approval: Triaged

 Description   

The spec generator associated with inst? may produce instants that are technically valid but cannot be read back (and also are not practical in reality).

(require '[clojure.spec.alpha :as s])

(second (last (s/exercise inst? 100)))
;; => #inst "883641-02-19T16:17:26.482-00:00"

#inst "883641-02-19T16:17:26.482-00:00"
;; => RuntimeException Unrecognized date/time syntax: 883641-02-19T16:17:26.482-00:00  clojure.instant/fn--7987/fn--7988 (instant.clj:107)

Minor issue, but I ran into this while interacting with inst generator/generated values in the REPL.



 Comments   
Comment by David Bürgin [ 12/May/18 3:53 AM ]

This problem gets worse when the inst generated is in the BC era. In that case, not even can the inst potentially not be read back, but since there is no indication of the ‘negative year’, information is lost.

Comment by Enzzo Cavallo [ 20/Feb/19 7:20 AM ]

There is some info about the limits of reader/printer in CLJ-2486
I catch this problem because I'm generating data and `pr-str` / `read-string` over the wire.





[CLJ-2336] [spec] Args to defn can be invalid, but produce nil explain-data Created: 16/Mar/18  Updated: 22/Jun/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Ben Brinckerhoff Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: spec

Approval: Triaged

 Description   
~  clj
Clojure 1.9.0
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (require '[clojure.core.specs.alpha])
nil
(let [spec (:args (s/get-spec `defn))
        form `(~'foo ([~'a] 1) [~'a ~'b])]
    {:valid (s/valid?
             spec
             form)
     :explain-data (s/explain-data
                    spec
                    form)})
{:valid false, :explain-data nil}
user=>

Expected: If form is invalid, explain-data should not be nil






[CLJ-2327] [spec] regex spec seems to exhibit exponential memory consumption Created: 26/Feb/18  Updated: 22/Jun/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Ghadi Shayban Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: spec

Approval: Triaged

 Description   

This example code will OOM on not very long input, and seems to get exponentially slower until then. If it's not a bug, it's a useful example for the test suite.

(s/def ::thing
  (s/+ (s/alt :x (s/+ (set (range 70))))))


(time (s/conform ::thing (range 10))) ;; 30ms
(time (s/conform ::thing (range 15))) ;; 500ms
(time (s/conform ::thing (range 20))) ;; 24s

Captured from user @petrus on Clojurians Slack, reduced repro by Max Penet.



 Comments   
Comment by Max Penet [ 27/Feb/18 8:39 AM ]

The example could be reduced to :

(s/def ::thing
  (s/+ (s/alt :x (s/+ (set (range 70))))))


(time (s/conform ::thing (range 10))) ;; 30ms
(time (s/conform ::thing (range 15))) ;; 500ms
(time (s/conform ::thing (range 20))) ;; 24s
Comment by Ghadi Shayban [ 27/Feb/18 8:59 AM ]

Thanks Max for the simplification!





[CLJ-2321] [spec] anonymous predicate in s/merge always fails Created: 01/Feb/18  Updated: 09/Jul/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: David Chelimsky Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec


 Description   

user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/def ::a int?)
:user/a
user=> (s/def ::b int?)
:user/b
user=> (s/def ::c (s/merge (s/keys :req [::a ::b]) #(< (::a %) (::b %))))
:user/c
user=> (s/explain-data ::c {::a 0 ::b 1})
#:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/< (:user/a %) (:user/b %))), :val #:user{:a 0, :b 1}, :via [:user/c], :in []}), :spec :user/c, :value #:user{:a 0, :b 1}}
user=> ;; ^^ not expected
user=> (s/explain-data ::c {::a 1 ::b 0})
#:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/< (:user/a %) (:user/b %))), :val #:user{:a 1, :b 0}, :via [:user/c], :in []}), :spec :user/c, :value #:user{:a 1, :b 0}}
user=> ;; ^^ expected
user=> (s/def ::a<b #(< (::a %) (::b %)))
:user/a<b
user=> (s/def ::d (s/merge (s/keys :req [::a ::b]) ::a<b))
:user/d
user=> (s/explain-data ::d {::a 0 ::b 1})
nil
user=> (s/explain-data ::d {::a 1 ::b 0})
#:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/< (:user/a %) (:user/b %))), :val #:user{:a 1, :b 0}, :via [:user/d :user/a<b], :in []}), :spec :user/d, :value #:user{:a 1, :b 0}}




 Comments   
Comment by David Chelimsky [ 01/Feb/18 4:58 PM ]

It also always fails when referring to a predicate function, so the only way to get it to work is to define another spec and refer to that.

user=> (defn a<b [a b] (< a b))
#'user/a<b
user=> (s/def ::e (s/merge (s/keys :req [::a ::b]) a<b))
:user/e
user=> (s/explain-data ::e {::a 1 ::b 2})
#:clojure.spec.alpha{:problems ({:path [], :pred user/a<b, :val #:user{:a 1, :b 2}, :via [:user/e], :in []}), :spec :user/e, :value #:user{:a 1, :b 2}}
user=> (s/explain-data ::e {::a 1 ::b 0})
#:clojure.spec.alpha{:problems ({:path [], :pred user/a<b, :val #:user{:a 1, :b 0}, :via [:user/e], :in []}), :spec :user/e, :value #:user{:a 1, :b 0}}
Comment by Alex Miller [ 01/Feb/18 4:58 PM ]

#(< (::a % ::b %)) doesn't seem right? Does #(< (::a %) (::b %)) work?

Comment by David Chelimsky [ 01/Feb/18 5:01 PM ]

Good catch, however, no #(< (::a %) (::b %)) does not work inline. I updated the example with to reflect that.

Comment by David Chelimsky [ 01/Feb/18 5:02 PM ]

Also, FWIW, I'd narrowed down the problem elsewhere, before my typo Just so happened that I got the same answer with and without the typo.

Comment by David Chelimsky [ 01/Feb/18 5:15 PM ]

It does work if you wrap the predicate in {s/spec}:

user=> (s/def ::f (s/merge (s/keys :req [::a ::b]) (s/spec #(< (::a %) (::b %)))))
:user/f
user=> (s/explain-data ::f {::a 1 ::b 2})
nil
user=> (s/explain-data ::f {::a 1 ::b 0})
#:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/< (:user/a %) (:user/b %))), :val #:user{:a 1, :b 0}, :via [:user/f], :in []}), :spec :user/f, :value #:user{:a 1, :b 0}}
Comment by jcr [ 09/Jul/18 5:54 PM ]

I'm unable to reproduce any of this.

In the issue description, the predicate #(< (::a %) (::b %)) require a to be less than b, so it is expected that {::a 1 ::b 0} doesn't match the spec.

In the first comment, the predicate a<b is invalid, since it accepts two numbers instead of a single map.

The following snippet works as expected too (there's no difference between anonymous pred, named pred and a pred wrapped in s/spec):

(s/def ::a number?)
(defn good? [m] (contains? m :a))

(def s1 (s/merge (s/keys :req-un [::a]) good?))
(def s2 (s/merge (s/keys :req-un [::a]) #(contains? % :a)))
(def s3 (s/merge (s/keys :req-un [::a]) (s/spec #(contains? % :a))))

(s/valid? s1 {:a 42}) ;=> true
(s/valid? s2 {:a 42}) ;=> true 
(s/valid? s3 {:a 42}) ;=> true

I believe the issue can be closed.

Comment by David Chelimsky [ 09/Jul/18 7:27 PM ]

When I updated the example after Alex's comment, I put the message in the wrong place. I just updated it again with the messages correctly aligning w/ the output. jcr, I do see that your examples work, but mine still do not. So this is not quite as general as all anonymous predicates, but there is still surprising behavior.

That said, s/merge docs say it supports keys specs, and does not claim to support any other type of predicate, so if you want to close it on those grounds, have at it.

Comment by jcr [ 09/Jul/18 9:41 PM ]

Ah, now I see what you mean. Sorry for jumping to conclusions.

So the actual problem here is that s/explain inconsistently reports inputs that are valid according to s/conform when s/merge is used with a predicate not wrapped in a s/spec call. Here's the minimal example I've managed to come up with:

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

(defn pred [m] (contains? m :a)) 
(s/def ::s (s/merge pred))

(def good {:a 42})
(def bad  {})

;; as expected
(s/conform ::s good) ;=> {:a 42}
(s/conform ::s bad)  ;=> :clojure.spec.alpha/invalid

;; !!!: first one should NOT fail
(s/explain-str ::s good) ;=> "val: {:a 42} fails spec: :test/s predicate: pred\n" 
(s/explain-str ::s bad)  ;=> "val: {} fails spec: :test/s predicate: pred\n"

;; explicitly wrap pred in spec
(s/def ::s* (s/merge (s/spec pred)))

;; now, works as expected
(s/explain-str ::s* good) ;=> "Success!\n"
(s/explain-str ::s* bad)  ;=> "val: {} fails spec: :test/s* predicate: pred\n"

I would suggest updating the title and the description accordingly, if you don't mind?

Comment by jcr [ 09/Jul/18 10:25 PM ]

As far as I can tell, the problem is that merge-spec-impl simply calls explain-1 with the given preds, and explain-1 always returns problems if (spec? pred) is false; merge-spec-impl should probably call specize on its preds, similar to how it's done in the tuple-impl:

(let [specs (delay (mapv specize preds forms)) ...] ...)




[CLJ-2320] [spec] Add support for a non-short-circuiting variant of clojure.spec.alpha/and Created: 01/Feb/18  Updated: 22/Jun/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: David Chelimsky Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec


 Description   

Consider this example:

$ clj
Clojure 1.9.0
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/def ::a int?)
:user/a
user=> (s/def ::b int?)
:user/b
user=> (s/def ::c int?)
:user/c
user=> (s/def ::d (s/and (s/keys :req [::a ::b ::c]) #(< (::a %) (::b %))))
:user/d
user=> (s/explain-data ::d {::a 1 ::b 0})
#:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/contains? % :user/c)), :val #:user{:a 1, :b 0}, :via [:user/d], :in []}), :spec :user/d, :value #:user{:a 1, :b 0}}
user=> (s/explain-data ::d {::a 1 ::b 0 ::c 2})
#:clojure.spec.alpha{:problems [{:path [], :pred (clojure.core/fn [%] (clojure.core/< (:user/a %) (:user/b %))), :val #:user{:a 1, :b 0, :c 2}, :via [:user/d], :in []}], :spec :user/d, :value #:user{:a 1, :b 0, :c 2}}

I'd like a way to get the first invocation of explain-data to include the missing ::c and the failing < predicate. I understand that spec/and is designed to short circuit, so perhaps this could be solved with a new function that processes n specs independently.



 Comments   
Comment by Alex Miller [ 01/Feb/18 8:51 AM ]

In this particular case, you can use s/merge and you'll get all problems...

user=> (s/def ::e (s/merge (s/keys :req [::a ::b ::c]) #(< (::a %) (::b %))))
:user/e
user=> (s/explain ::e {::a 1 ::b 0})
val: #:user{:a 1, :b 0} fails spec: :user/e predicate: (contains? % :user/c)
val: #:user{:a 1, :b 0} fails spec: :user/e predicate: (< (:user/a %) (:user/b %))

s/merge does have the semantics of "satisfies all", but is of course specific to maps.

Comment by David Chelimsky [ 01/Feb/18 9:07 AM ]

Got it. The other part of my specific problem, not reflected in this example, is that I've got a conformer in the middle that the last predicate relies on. s/merge won't work in that case because the conformer can't generate. Would you like a separate issue for that?

Comment by David Chelimsky [ 01/Feb/18 9:23 AM ]

So I've got this close to working with a custom generator. Do you want to leave this issue open?

Comment by Alex Miller [ 01/Feb/18 9:37 AM ]

Re the conformer - no, it's by design that conformed values don't flow in s/merge (and is in line with the notion of "not flowing" that goes with "satisfies all"). Re leaving it open, that's up to you. We have talked about creating a non-flowing and variant but I don't think there's a ticket for it.

Comment by David Chelimsky [ 01/Feb/18 3:26 PM ]

Actually, I think I just discovered a bug related to this. Following on the examples above:

user=> (s/explain ::e {::a 1 ::b 2})
val: #:user{:a 1, :b 2} fails spec: :user/e predicate: (contains? % :user/c)
val: #:user{:a 1, :b 2} fails spec: :user/e predicate: (< (:user/a %) (:user/b %))
nil

This should pass (< (:user/a %) (:user/b %))

Comment by David Chelimsky [ 01/Feb/18 3:26 PM ]

I'll file a separate issue for that.





[CLJ-2311] [spec] Spec generator override won't work on multi-spec dispatch key Created: 12/Jan/18  Updated: 22/Jun/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Minor
Reporter: Andreas Liljeqvist Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: generator, spec
Environment:

[org.clojure/clojure "1.9.0"]
[org.clojure/spec.alpha "0.1.143"]


Attachments: Text File CLJ-2311.patch     File multi_spec_bug.clj    
Patch: Code
Approval: Triaged

 Description   

Generator override doesn't work as expected on multi-specs.
Code below illustrates the problem.

(s/def ::obj-type #{:a :b})

(s/def ::base-obj (s/keys :req [::obj-type]))

(defmulti obj-type ::obj-type)
(defmethod obj-type :a [_]
  ::base-obj)
(defmethod obj-type :b [_]
  ::base-obj)

(s/def ::obj (s/multi-spec obj-type ::obj-type))
(gen/sample (s/gen ::obj {::obj-type #(gen/return :a)}))

In the example above the dispatch-fn ::obj-type for the multimethod is given a generator override.
It is expected to return only colls of {::obj-type :a}
Actually it will also return {::obj-type :b}.
That is a generator cannot be used to constrain the set of dispatch-keys to sample from.

Current method:

In the case of a multimethod a generator is constructed for every possible dispatch value.
One is then chosen randomly without paying any attention to overrides for the dispatch-fn(key).

Patched method:

Commit available here
Patched version constructs generators for dispatch values exactly as original.
After that a check is made to see if there exists an override for the dispatch-fn.
If so a gen/bind is done using the override generator.
The bind function generates a value from the override generator.
That value is then used to lookup and return the correct multimethod generator.

Test case



 Comments   
Comment by Andreas Liljeqvist [ 09/Mar/18 9:35 AM ]

Patch provided for issue.

Comment by Alex Miller [ 09/Mar/18 9:48 AM ]

Can you move the code for the problem into the description and explain the problem and the change?

Comment by Andreas Liljeqvist [ 09/Mar/18 10:17 AM ]

I can't figure out how to edit the description...

description:
In the case of a multimethod a generator is constructed for every possible dispatch value.
One is then choosen randomly without paying any attention to overrides for the dispatch-fn(key).

commit: https://github.com/bonega/spec.alpha/commit/9cb42478b52eac275d496ec29669e2bf4b3e8e1f
My patch hopefully fixes that by checking if there exists an override for the dispatch-fn.
If so a gen/bind is done to the override generator and the result is used to lookup the correct multimethod generator.

Test case: https://pastebin.com/62ZT5Zfc
The test is expected to pass.
Basically I want to generate ::obj with an overriding generator like `(gen/generate (s/gen ::obj {::obj-type #(gen/return :a)}))`.
Expected result should always be `{::obj-type :a}`.
Previously :b was possible as output even though an override was specified.

Comment by Alex Miller [ 09/Mar/18 2:01 PM ]

I've given you edit right for the ticket so you can make the updates...

Comment by Andreas Liljeqvist [ 12/Mar/18 7:35 AM ]

I have updated the description.
Let me know if anything is unclear.





[CLJ-2304] [spec] spec passing nil as second argument to `ExceptionInfo` constructor... Created: 02/Jan/18  Updated: 22/Jun/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Jonathan Leonard Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: spec
Environment:

Mac OS X 10.13.2

jonathan$ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)


Attachments: Text File better-messaging.patch    

 Description   

Exception thrown from this line:
https://github.com/clojure/clojure/blob/clojure-1.9.0/src/jvm/clojure/lang/ExceptionInfo.java#L31

Due to this call:
https://github.com/clojure/spec.alpha/blob/spec.alpha-0.1.143/src/main/clojure/clojure/spec/test/alpha.clj#L279

if `when-not` returns nil, `ExceptionInfo` throws exception on line 31.

A naive fix may be:
(apply ex-info (remove nil? ["Specification-based check failed" (when-not ...)]))

although that is really just masking over the issue and provides no actionable info to the sufferer of the failure.

Unfortunately I have no minimal test case as this is proprietary (and complicated code).

Perhaps the author from reading the code will have more insight.

Here's the full stack trace:

[[clojure.lang.ExceptionInfo <init> "ExceptionInfo.java" 31]
[clojure.lang.ExceptionInfo <init> "ExceptionInfo.java" 22]
[clojure.core$ex_info invokeStatic "core.clj" 4739]
[clojure.core$ex_info invoke "core.clj" 4739]
[clojure.spec.test.alpha$explain_check invokeStatic "alpha.clj" 277]
[clojure.spec.test.alpha$explain_check invoke "alpha.clj" 275]
[clojure.spec.test.alpha$check_call invokeStatic "alpha.clj" 295]
[clojure.spec.test.alpha$check_call invoke "alpha.clj" 285]
[clojure.spec.test.alpha$quick_check$fn__2986 invoke "alpha.clj" 308]
[clojure.lang.AFn applyToHelper "AFn.java" 154]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.core$apply invokeStatic "core.clj" 657]
[clojure.core$apply invoke "core.clj" 652]
[clojure.test.check.properties$apply_gen$fn__16139$fn__16140 invoke "properties.cljc" 30]
[clojure.test.check.properties$apply_gen$fn__16139 invoke "properties.cljc" 29]
[clojure.test.check.rose_tree$fmap invokeStatic "rose_tree.cljc" 77]
[clojure.test.check.rose_tree$fmap invoke "rose_tree.cljc" 73]
[clojure.test.check.generators$fmap$fn__9199 invoke "generators.cljc" 101]
[clojure.test.check.generators$gen_fmap$fn__9173 invoke "generators.cljc" 57]
[clojure.test.check.generators$call_gen invokeStatic "generators.cljc" 41]
[clojure.test.check.generators$call_gen invoke "generators.cljc" 37]
[clojure.test.check$quick_check invokeStatic "check.cljc" 94]
[clojure.test.check$quick_check doInvoke "check.cljc" 37]
[clojure.lang.RestFn invoke "RestFn.java" 425]
[clojure.lang.AFn applyToHelper "AFn.java" 156]
[clojure.lang.RestFn applyTo "RestFn.java" 132]
[clojure.core$apply invokeStatic "core.clj" 657]
[clojure.core$apply invoke "core.clj" 652]
[clojure.spec.gen.alpha$quick_check invokeStatic "alpha.clj" 29]
[clojure.spec.gen.alpha$quick_check doInvoke "alpha.clj" 27]
[clojure.lang.RestFn applyTo "RestFn.java" 137]
[clojure.core$apply invokeStatic "core.clj" 661]
[clojure.core$apply invoke "core.clj" 652]
[clojure.spec.test.alpha$quick_check invokeStatic "alpha.clj" 309]
[clojure.spec.test.alpha$quick_check invoke "alpha.clj" 302]
[clojure.spec.test.alpha$check_1 invokeStatic "alpha.clj" 335]
[clojure.spec.test.alpha$check_1 invoke "alpha.clj" 323]
[clojure.spec.test.alpha$check$fn__3005 invoke "alpha.clj" 411]
[clojure.core$pmap$fn__8105$fn__8106 invoke "core.clj" 6942]
[clojure.core$binding_conveyor_fn$fn__5476 invoke "core.clj" 2022]
[clojure.lang.AFn call "AFn.java" 18]
[java.util.concurrent.FutureTask run "FutureTask.java" 266]
[java.util.concurrent.ThreadPoolExecutor runWorker "ThreadPoolExecutor.java" 1149]
[java.util.concurrent.ThreadPoolExecutor$Worker run "ThreadPoolExecutor.java" 624]
[java.lang.Thread run "Thread.java" 748]]



 Comments   
Comment by Jonathan Leonard [ 02/Jan/18 12:47 PM ]

So, I was able to finally track this down to an actual problem where my function value returned from the function under test would throw and exception on a particular input.

This patch helps point people in that direction (although an even better one would be to surface/explain the underlying failure rather than just allude to it).

Comment by Jonathan Leonard [ 01/Feb/18 2:50 PM ]

Anyone going to respond to this? What's the usual expected response time for feedback on a patch/pull request?

Comment by Alex Miller [ 01/Feb/18 2:58 PM ]

There is not enough information here for this ticket to be actionable. If (s/valid? spec data) is false, then (s/explain-data spec data) should never be nil. The fact that you're seeing that is indication of a bug in the spec implementation, but without more info about the spec or the data, there's nothing I can do at the moment.

Comment by Jonathan Leonard [ 01/Feb/18 3:35 PM ]

The "actionable" part is the submitted patch. This code is more resilient and friendly to sufferers of the issue.

The actual text of this ticket is merely a CLONE of the original (since I could not re-open or edit the original).

Comment by Jonathan Leonard [ 01/Feb/18 3:37 PM ]

i.e., the only way I could submit this patch was to create a CLONE.

Comment by Andy Fingerhut [ 02/Feb/18 12:37 AM ]

Jonathan, since you are on the list of Clojure contributors, I have bumped up your permissions on Clojure JIRA so you should be able to edit tickets now.

I do not think there is a 'usual time' by which tickets are responded to. It can vary by orders of magnitude, depending upon clarity and perceived criticality of the issue.

Alex would probably appreciate if the description included an example that can be evaluated in order to reproduce the problem. Often by seeing the problem, they may consider other solutions to the problem.

Comment by Alex Miller [ 02/Feb/18 7:18 AM ]

The patch is addressing the symptom, not the problem. We really need the spec and data value causing the issue.

Comment by Jonathan Leonard [ 02/Feb/18 3:38 PM ]

The patch does exactly what it claims to do: provide better messaging for this scenario.

Unfortunately my attempt at constructing a minimal repro from scratch was not successful. I understand that you would like me to do additional work but all work that I've done up to this point is pro bono and I cannot donate any more of my time to this cause at this time.

We should discuss the merits of this patch on its own-- is it an improvement over the previous code or not? [And static human analysis of the code can determine that it is in fact an improvement]. The root "problem" here is that `s/valid?` and `s/explain-data` are not pure, total functions-- they can fail probabilistically depending on the arguments (i.e., when there is a higher-order function involved [which due to a fundamental limitation of spec can only be "verified" by passing random data through it]). In fact, I cannot imagine any better way to address this issue given that limitation (except bubbling up the exception which was certainly swallowed in my original, real-world reproduction of this but which was not in my attempt at the minimal repro).

Comment by Jonathan Leonard [ 08/Mar/18 1:19 PM ]

I would appreciate a response to the provided reasoning.

Comment by Ghadi Shayban [ 08/Mar/18 7:33 PM ]

Without a reproduction case, I'm afraid this will not move forward. If there is an invariant being broken (e.g. invalid data but missing explain-data) provide a example. It doesn't have to be minimal, just needs to be reproducible.

[1] https://dev.clojure.org/display/community/Creating+Tickets
[2] https://dev.clojure.org/display/community/Developing+Patches (see Before You Code)

Comment by Jonathan Leonard [ 08/Mar/18 7:49 PM ]

So, are you saying that the Clojure project does not accept bug reports that were discovered by manual human inspection? Because if it does accept such, then you should consider this bug to be a case of such.

Also, if you would allow yourselves to merely think about the two functions in question, you can "discover" the bug for yourselves. Do you accept that `s/explain-data` and `s/valid?` are not purely deterministic? i.e., they can return different results than both each other and themselves given the exact same input on two successive runs. If you do accept this, then you have convinced yourselves of the validity of this bug claim (and I strongly urge you to accept that because it is the reality of the situation).

Comment by Ghadi Shayban [ 08/Mar/18 8:20 PM ]

I am trying to help you formulate a better, actionable ticket. I don't speak on behalf of 'the Clojure project'. I implore you to tone down the antagonism. Language like "if you would allow yourselves to merely think about the two functions in question" doesn't belong here.

I don't doubt that you've uncovered an issue, but as it stands this is unactionable.

Comment by Jonathan Leonard [ 08/Mar/18 9:01 PM ]

I think you should go back and re-read my previous message. Imagine that it were spoken calmly, plainly and matter-of-factly because that is how it was written/intended (instead of however you have chosen to read it). Also, it was not new information— merely a rewording of previous communications (which do not seem to have been understood).

And I really do not appreciate the sanctimonious, condescending, patronizing in your previous message to me. Save the moral grandstanding for somewhere more appropriate (wherever that may be). It has no place in a technical bug report.

Comment by Alex Miller [ 09/Mar/18 8:47 AM ]

Regarding the non-determinism, that's a known issue being tracked in CLJ-1949. Assuming that's addressed, I don't see the need for this change. However, I'm not sure where all that would go so I'm just going to leave this ticket open and defer to later when that becomes clearer.

Comment by Jonathan Leonard [ 09/Mar/18 3:07 PM ]

Ah, yes, if CLJ-1949 were fixed then this would not be an issue. Thanks for the (reasonable) explanation.





[CLJ-2300] `explain-data` returns `nil` reason for IFn spec failure (which seemingly shouldn't be a failure to begin with) Created: 21/Dec/17  Updated: 21/Dec/17  Resolved: 21/Dec/17

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Jonathan Leonard Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: bug, spec
Environment:

Mac OS X 10.13.2

jonathan$ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)



 Description   
user> (require '[clojure.spec.alpha :as s])
nil
user> (defn printable-fn [f to-string]
  (reify clojure.lang.IFn
    (toString [this] (to-string))
    (invoke [this a] (f a))))
#'user/printable-fn
user> (defn printable-const-fn [constant]
  (printable-fn
   (fn [_] constant)
   #(format "(fn [_] %s)" (if (string? constant) (format "\"%s\"" constant) constant))))
#'user/printable-const-fn
user> (s/explain-data (s/fspec :args (s/cat :a any?) :ret boolean?) (printable-const-fn true))
#:clojure.spec.alpha{:problems [{:path [], :pred (apply fn), :val (0), :reason nil, :via [], :in []}], :spec #object[clojure.spec.alpha$fspec_impl$reify__2451 0x1479eb26 "clojure.spec.alpha$fspec_impl$reify__2451@1479eb26"], :value #object[user$printable_fn$reify__35128 0x74e99361 "(fn [_] true)"]}
user> (clojure.pprint/pprint *1)
#:clojure.spec.alpha{:problems
                     [{:path [],
                       :pred (apply fn),
                       :val (0),
                       :reason nil,
                       :via [],
                       :in []}],
                     :spec
                     #object[clojure.spec.alpha$fspec_impl$reify__2451 0x1479eb26 "clojure.spec.alpha$fspec_impl$reify__2451@1479eb26"],
                     :value
                     #object[user$printable_fn$reify__35128 0x74e99361 "(fn [_] true)"]}
nil
user>


 Comments   
Comment by Alex Miller [ 21/Dec/17 2:29 PM ]

IFn also has `applyTo` and spec will try to invoke the function using `apply`.

Use this:

(defn printable-fn [f to-string]
  (reify clojure.lang.IFn
    (toString [this] (to-string))
    (invoke [this a] (f a))
    (applyTo [this x] (apply f x))))  ;; added
Comment by Alex Miller [ 21/Dec/17 2:29 PM ]

Invalid function implementation

Comment by Jonathan Leonard [ 21/Dec/17 2:35 PM ]

Ah, thank you!

FWIW, I grabbed that code from:
https://stackoverflow.com/questions/5306015/equivilent-of-javas-tostring-for-clojure-functions/5306202#5306202

[left a comment there requesting the update]

Comment by Jonathan Leonard [ 21/Dec/17 2:38 PM ]

Is the fact that this reports a `nil` reason though not something that may want to be improved? [I likely would have figured it out on my own if it weren't for that fact. :) ]

Comment by Alex Miller [ 21/Dec/17 2:42 PM ]

:reason is an optional key and isn't always set.





[CLJ-2299] spec passing nil as second argument to `ExceptionInfo` constructor... Created: 21/Dec/17  Updated: 21/Dec/17  Resolved: 21/Dec/17

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Jonathan Leonard Assignee: Unassigned
Resolution: Not Reproducible Votes: 0
Labels: bug, spec
Environment:

Mac OS X 10.13.2

jonathan$ java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)



 Description   

Exception thrown from this line:
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/ExceptionInfo.java#L31

Due to this call:
https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/test/alpha.clj#L279

if `when-not` returns nil, `ExceptionInfo` throws exception on line 31.

A naive fix may be:
(apply ex-info (remove nil? ["Specification-based check failed" (when-not ...)]))

although that is really just masking over the issue and provides no actionable info to the sufferer of the failure.

Unfortunately I have no minimal test case as this is proprietary (and complicated code).

Perhaps the author from reading the code will have more insight.

Here's the full stack trace:

[[clojure.lang.ExceptionInfo <init> "ExceptionInfo.java" 31]
[clojure.lang.ExceptionInfo <init> "ExceptionInfo.java" 22]
[clojure.core$ex_info invokeStatic "core.clj" 4739]
[clojure.core$ex_info invoke "core.clj" 4739]
[clojure.spec.test.alpha$explain_check invokeStatic "alpha.clj" 277]
[clojure.spec.test.alpha$explain_check invoke "alpha.clj" 275]
[clojure.spec.test.alpha$check_call invokeStatic "alpha.clj" 295]
[clojure.spec.test.alpha$check_call invoke "alpha.clj" 285]
[clojure.spec.test.alpha$quick_check$fn__2986 invoke "alpha.clj" 308]
[clojure.lang.AFn applyToHelper "AFn.java" 154]
[clojure.lang.AFn applyTo "AFn.java" 144]
[clojure.core$apply invokeStatic "core.clj" 657]
[clojure.core$apply invoke "core.clj" 652]
[clojure.test.check.properties$apply_gen$fn__16139$fn__16140 invoke "properties.cljc" 30]
[clojure.test.check.properties$apply_gen$fn__16139 invoke "properties.cljc" 29]
[clojure.test.check.rose_tree$fmap invokeStatic "rose_tree.cljc" 77]
[clojure.test.check.rose_tree$fmap invoke "rose_tree.cljc" 73]
[clojure.test.check.generators$fmap$fn__9199 invoke "generators.cljc" 101]
[clojure.test.check.generators$gen_fmap$fn__9173 invoke "generators.cljc" 57]
[clojure.test.check.generators$call_gen invokeStatic "generators.cljc" 41]
[clojure.test.check.generators$call_gen invoke "generators.cljc" 37]
[clojure.test.check$quick_check invokeStatic "check.cljc" 94]
[clojure.test.check$quick_check doInvoke "check.cljc" 37]
[clojure.lang.RestFn invoke "RestFn.java" 425]
[clojure.lang.AFn applyToHelper "AFn.java" 156]
[clojure.lang.RestFn applyTo "RestFn.java" 132]
[clojure.core$apply invokeStatic "core.clj" 657]
[clojure.core$apply invoke "core.clj" 652]
[clojure.spec.gen.alpha$quick_check invokeStatic "alpha.clj" 29]
[clojure.spec.gen.alpha$quick_check doInvoke "alpha.clj" 27]
[clojure.lang.RestFn applyTo "RestFn.java" 137]
[clojure.core$apply invokeStatic "core.clj" 661]
[clojure.core$apply invoke "core.clj" 652]
[clojure.spec.test.alpha$quick_check invokeStatic "alpha.clj" 309]
[clojure.spec.test.alpha$quick_check invoke "alpha.clj" 302]
[clojure.spec.test.alpha$check_1 invokeStatic "alpha.clj" 335]
[clojure.spec.test.alpha$check_1 invoke "alpha.clj" 323]
[clojure.spec.test.alpha$check$fn__3005 invoke "alpha.clj" 411]
[clojure.core$pmap$fn__8105$fn__8106 invoke "core.clj" 6942]
[clojure.core$binding_conveyor_fn$fn__5476 invoke "core.clj" 2022]
[clojure.lang.AFn call "AFn.java" 18]
[java.util.concurrent.FutureTask run "FutureTask.java" 266]
[java.util.concurrent.ThreadPoolExecutor runWorker "ThreadPoolExecutor.java" 1149]
[java.util.concurrent.ThreadPoolExecutor$Worker run "ThreadPoolExecutor.java" 624]
[java.lang.Thread run "Thread.java" 748]]



 Comments   
Comment by Alex Miller [ 21/Dec/17 2:38 PM ]

The assumption made in the code here is that this function is only called when either valid? or conform has found that the value does not conform to the spec. In all cases, the same inputs should return an explain-data when requested. So, those assumptions are correct and this code is also correct.

However, clearly you have encountered a scenario where the spec conform is not holding to that and that is a bug. Unfortunately, I have no way to reproduce it, so I have to close this ticket as not reproducible. If you are able to reproduce this with an example spec+value, I'd be happy to re-open.

Comment by Jonathan Leonard [ 21/Dec/17 2:44 PM ]

Fair enough. I will work on getting a reproduction (which is what the `printable-fn` wrapper from the other ticket is intended to help with).





[CLJ-2294] [spec] Allow user to get forms of multi-spec implementations Created: 14/Dec/17  Updated: 22/Jun/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Ben Brinckerhoff Assignee: Unassigned
Resolution: Unresolved Votes: 0
Labels: spec
Environment:

org.clojure/spec.alpha "0.1.143"



 Description   

As noted in this thread (https://groups.google.com/forum/#!topic/clojure/i8Rz-AnCoa8),

s/keys
does not validate that the specs mentioned within it are valid specs.

A suggested workaround is to separately write code that could inspect the registry and warn about specs that are not declared. A sketch of this code was provided at https://gist.github.com/stuarthalloway/f4c4297d344651c99827769e1c3d34e9

While the general approach is a good one, I believe it won't work for multi-specs that contain "keys" specs.

s/form
on a multi-spec will only return that the spec is a multi-spec (which makes sense), but there is no way for a client to get access to code that picks the approach spec given data.

A further workaround is provided in the comments to that gist, but since it relies on

resolve
, it will not work on Clojurescript.

I think

s/form
is behaving correctly in this case, but it would be useful to have an additional function that, given a multi-spec, could return a function or data that could be used to explore the current implementations.






[CLJ-2288] Add :into parameter to s/keys for spec Created: 12/Dec/17  Updated: 13/Dec/17  Resolved: 13/Dec/17

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.9
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Leon Mergen Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: spec


 Description   

As with other spec functions that operate on similar data structures (e.g. map-of), in some situations it can be desirable to control the exact collection type generated by s/gen (for example, when you require a sorted-map).

While you can work around it with writing a custom :gen generator, a simpler solution for the end-user might be to add a :into parameter for s/keys.

If a patch for this would be accepted, I have no problem contributing it.



 Comments   
Comment by Alex Miller [ 13/Dec/17 8:13 AM ]

Not interested, thanks.





[CLJ-2277] [spec] Add fully-qualified specs to explain-data when map fails to contain required keys Created: 28/Nov/17  Updated: 22/Jun/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Ben Brinckerhoff Assignee: Unassigned
Resolution: Unresolved Votes: 1
Labels: spec
Environment:

org.clojure/spec.alpha "0.1.143"



 Description   

Consider the following spec

(s/def :example/name string?)
(s/def :example/age pos-int?)
(s/def :wine/age (s/and pos-int?
                        #(< 100)))
(s/def :example/employee (s/keys
                          :req [:example/string]
                          :req-un [:example/age]))

(s/explain-data :example/employee {})
;; #:clojure.spec.alpha{:problems ({:path [], :pred (clojure.core/fn [%] (clojure.core/contains? % :example/string)), :val {}, :via [:example/employee], :in []} {:path [], :pred (clojure.core/fn [%] (clojure.core/contains? % :age)), :val {}, :via [:example/employee], :in []}), :spec :example/employee, :value {}}

The explain-data in this case does give the precise problem: the map is required keys. But the user requires at least two actions to resolve the problem with ":age": either add some fake value for the required keys and look at the new explain-data OR guess which :age spec was intended and look up the spec.

The default "explain" message is fine in this case, but other spec printers may want to give users hints on how they can resolve this problem in one step. This is easy in the case of ":req" because the spec is fully qualified, but not possible in general for ":req-un".

A solution is to include the fully-qualified spec along with the failing predicate in the "problem" data above.






[CLJ-2273] [spec] Add original 'assert' form to explain-data for s/assert Created: 21/Nov/17  Updated: 22/Jun/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Enhancement Priority: Major
Reporter: Ben Brinckerhoff Assignee: Unassigned
Resolution: Unresolved Votes: 2
Labels: spec
Environment:

org.clojure/spec.alpha "0.1.143"


Approval: Triaged

 Description   

Using s/assert instead of assert has advantages: namely a specific reason why the data failed to meet the spec. But it also has disadvantages: the error message does not contain the assertion code the user wrote, so it's harder to see which assertion failed.

I believe we could have the best of both world if s/assert contained the original "assert" code - that way the error message could (optionally) print out this information.

(require '[clojure.spec.alpha :as s])
  (s/check-asserts true)

  (let [x 1]
    (s/assert string? x))

  ;; Spec assertion failed val: 1 fails predicate:
  ;; :clojure.spec.alpha/unknown

  (let [x 1]
    (assert (string? x)))

  ;; Assert failed: (string? x)





[CLJ-2271] [spec] "caller" information missing in explain-data during macro instrumentation failure Created: 19/Nov/17  Updated: 26/Nov/18

Status: Open
Project: Clojure
Component/s: None
Affects Version/s: Release 1.10
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Ben Brinckerhoff Assignee: Unassigned
Resolution: Unresolved Votes: 8
Labels: error-reporting, spec
Environment:

org.clojure/spec.alpha "0.1.143"


Approval: Triaged

 Description   

When there is a instrumentation failure for a function, the explain-data includes "caller" information. However, this information is missing if the instrumentation failure is for a macro.

This comment has led me to believe that the intended behavior is for explain-data to contain this info, so third-party error printers can display it.

In the repro below, I'm setting up a custom printer just to capture the raw explain-data (it's not a useful printer, just a means to show what is happenening)

Repro:

(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}}

  ;; ^--- Note there is an entry for :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 ("")}

  ;; ^--- No caller information


 Comments   
Comment by Alex Miller [ 27/Nov/17 8:39 AM ]

You can't instrument a macro, so that part of the ticket doesn't make sense as written. But I expect you mean the spec check during macro expansion.

In the macro check case, the caller info is known by the compiler and included in the wrapper CompilerException. I suppose that info could be passed into s/macroexpand-check from the Compiler and possibly produce similar results as with instrumented function calls.

Comment by Ben Brinckerhoff [ 19/Mar/18 6:39 PM ]

Ah, you're correct, thanks for clarifying.

If caller information was added, it would also be very useful to add a specific `:clojure.spec.alpha/failure` value for specs that fail during macro expansion. That would allow 3rd party tools to show specific types of error messages when macro expansion failed.

Comment by Ben Brinckerhoff [ 19/Mar/18 6:41 PM ]

Additionally, having access to the symbol for the macro name would assist in error reporting.

Comment by Shogo Ohta [ 27/Mar/18 9:54 PM ]

IMHO explain-data for instrumentation failures and macro spec errors should contain the fspec of the function (or macro), as I suggested before in CLJ-2218.

An fspec has some useful info (such as the ns-qualified name symbol and the line number etc.) of the spec'ed function in its metadata, so spec error reporters can use them for richer and more precise error messages in a uniform way for both instrumentation failures and macro spec errors.

Comment by Alex Miller [ 22/Aug/18 9:44 AM ]

After CLJ-2373, the caller information for spec macro failures will be conveyed in a wrapper CompilerException (as with other compiler and macroexpansion errors). Should consider whether this issue is effectively resolved as a result.

Comment by Ben Brinckerhoff [ 22/Aug/18 10:02 AM ]

I'm glad to hear this will be included in the CompilerException, but I think it still is useful to include in the spec explain-data.

When printing a spec error, it would be clearer if macro expansion spec errors and instrumentation errors could be printed in a similar format (if the user chooses a custom spec printer). It may be disruptive to look for the caller information in one place for macro expansion errors and a different place for instrumentation errors.

Furthermore, spec printers may choose to do novel things with this caller information like adding color to output or printing the actual code instead of the line numbers. It'd be nice to do have all this information available for the spec printer to customize.

Comment by