Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add specs to analyzer, add spec based tests #238

Merged
merged 42 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
1f8da01
(wip)
swannodette Oct 23, 2024
6087f8e
(wip)
swannodette Oct 23, 2024
56a6721
* typos
swannodette Oct 23, 2024
8d8a55a
* rest of the bits from the ast-ref
swannodette Oct 23, 2024
b28addc
* add spec tests ns
swannodette Oct 24, 2024
2009627
* fix assertion, another one
swannodette Oct 24, 2024
9f988d8
* add basic do
swannodette Oct 24, 2024
2a8db4d
* :binding missing :form
swannodette Oct 25, 2024
fd5b19f
* :binding :shadow can be nil
swannodette Oct 25, 2024
45bc823
* :case-node is synthetic - no :form
swannodette Oct 25, 2024
68e120f
* add tests for new
swannodette Oct 25, 2024
2bd179a
* add test-throw
swannodette Oct 25, 2024
83dadc9
* some :binding node missing :env and :form
swannodette Oct 25, 2024
55c8b12
* const tests
swannodette Oct 25, 2024
3e630af
* check the :op
swannodette Oct 25, 2024
2cc8c5c
* remove unneeded warnings
swannodette Oct 29, 2024
d0ece88
* add deftype
swannodette Nov 1, 2024
3bf3b4e
* host field & call
swannodette Nov 1, 2024
8822838
* add invoke
swannodette Nov 1, 2024
ac8b0de
* ::node not ::nodes, fixes stackoverflow
swannodette Nov 1, 2024
6a353c3
* add missing :js spec
swannodette Nov 1, 2024
69c0f84
* alphabetize specs
swannodette Nov 15, 2024
fb836ea
* alphabetize tests
swannodette Nov 15, 2024
2c92f31
* :binding
swannodette Nov 15, 2024
66487bb
* :op :quote should be :literal? true
swannodette Nov 17, 2024
6226322
* add binding, note about list, add set, typo
swannodette Nov 17, 2024
17c79e7
* add basic :ns & :ns* specs
swannodette Nov 17, 2024
6879174
* ns* stest
swannodette Nov 17, 2024
70b16c7
* add js-array
swannodette Nov 17, 2024
4cb5d04
* test-js-var
swannodette Nov 17, 2024
5219786
* test-the-var
swannodette Nov 17, 2024
e888fae
* missing assertion
swannodette Nov 17, 2024
82efd24
* more assertions for case
swannodette Nov 18, 2024
4d7210e
* case-node, case-test, case-then
swannodette Nov 18, 2024
cf2d7fc
* fn-method
swannodette Nov 18, 2024
a91025a
* test-local
swannodette Nov 20, 2024
1ce0ca7
* test-var
swannodette Nov 20, 2024
0ad7f04
* missing information from try/catch processing, we need to add `:loc…
swannodette Nov 20, 2024
125b4a2
* clarify comment
swannodette Nov 20, 2024
867a26b
* test-js
swannodette Nov 20, 2024
7bf09f0
* :no-op only has :env & :op
swannodette Nov 20, 2024
eb1b4a6
* :js-object :keys are _not_ nodes
swannodette Nov 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
:main-opts ["-i" "src/test/cljs_cli/cljs_cli/test_runner.clj"
"-e" "(cljs-cli.test-runner/-main)"]}
:compiler.test {:extra-paths ["src/test/cljs" "src/test/cljs_build" "src/test/cljs_cp"
"src/test/clojure" "src/test/self"]}
"src/test/clojure" "src/test/self"]
:extra-deps {org.clojure/spec.alpha {:mvn/version "0.5.238"}}}
:compiler.test.run {:main-opts ["-i" "src/test/clojure/cljs/test_runner.clj"
"-e" "(cljs.test-runner/-main)"]}
:runtime.test.build {:extra-paths ["src/test/cljs"]
Expand Down
26 changes: 19 additions & 7 deletions src/main/clojure/cljs/analyzer.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -1880,7 +1880,12 @@
(assoc locals e
{:name e
:line (get-line e env)
:column (get-col e env)})
:column (get-col e env)
;; :local is required for {:op :local ...} nodes
;; but previously we had no way to figure this out
;; for `catch` locals, by adding it here we can recover
;; it later
:local :catch})
locals)
catch (when cblock
(disallowing-recur (analyze (assoc catchenv :locals locals) cblock)))
Expand Down Expand Up @@ -2143,6 +2148,7 @@
{:line line :column column})
param {:op :binding
:name name
:form name
:line line
:column column
:tag tag
Expand Down Expand Up @@ -2205,8 +2211,10 @@
shadow (or (handle-symbol-local name (get locals name))
(get-in env [:js-globals name]))
fn-scope (:fn-scope env)
name-var {:name name
:op :binding
name-var {:op :binding
:env env
:form name
:name name
:local :fn
:info {:fn-self-name true
:fn-scope fn-scope
Expand Down Expand Up @@ -2326,8 +2334,10 @@
(let [ret-tag (-> n meta :tag)
fexpr (no-warn (analyze env (n->fexpr n)))
be (cond->
{:name n
:op :binding
{:op :binding
:name n
:form n
:env env
:fn-var true
:line (get-line n env)
:column (get-col n env)
Expand Down Expand Up @@ -2416,7 +2426,9 @@
col (get-col name env)
shadow (or (handle-symbol-local name (get-in env [:locals name]))
(get-in env [:js-globals name]))
be {:name name
be {:op :binding
:name name
:form name
:line line
:column col
:init init-expr
Expand All @@ -2425,7 +2437,6 @@
:shadow shadow
;; Give let* bindings same shape as var so
;; they get routed correctly in the compiler
:op :binding
:env {:line line :column col}
:info {:name name
:shadow shadow}
Expand Down Expand Up @@ -2565,6 +2576,7 @@
(throw (error env "Wrong number of args to quote")))
(let [expr (analyze-const env x)]
{:op :quote
:literal? true
:expr expr
:env env
:form form
Expand Down
288 changes: 288 additions & 0 deletions src/test/clojure/cljs/analyzer/spec_tests.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
;; Copyright (c) Rich Hickey. All rights reserved.
;; The use and distribution terms for this software are covered by the
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; which can be found in the file epl-v10.html at the root of this distribution.
;; By using this software in any fashion, you are agreeing to be bound by
;; the terms of this license.
;; You must not remove this notice, or any other, from this software.

(ns cljs.analyzer.spec-tests
(:require [cljs.analyzer :as ana]
[cljs.analyzer.api :as ana-api :refer [no-warn]]
[cljs.compiler.api :as comp-api]
[cljs.analyzer-tests :refer [analyze ns-env]]
[cljs.analyzer.specs :as a]
[clojure.test :as test :refer [deftest is]]
[clojure.spec.alpha :as s])
(:import [java.io StringReader]))

(deftest test-binding
(let [node (analyze ns-env '(let [x 1] x))
binding (-> node :bindings first)]
(is (= :binding (:op binding)))
(is (s/valid? ::a/node binding))))

(deftest test-case
(let [let-node (no-warn (analyze ns-env '(case x 1 :foo 2 :bar)))
node (-> let-node :body :ret)]
(is (= :case (:op node)))
(is (s/valid? ::a/node node))
(let [nodes (-> node :nodes)
case-node (first nodes)]
(is (= :case-node (:op case-node)))
(is (s/valid? ::a/node case-node))
(let [case-tests (:tests case-node)
case-test (first case-tests)
case-then (:then case-node)]
(is (= :case-test (:op case-test)))
(is (s/valid? ::a/node case-test))
(is (= :case-then (:op case-then)))
(is (s/valid? ::a/node case-then))))))

(deftest test-const
(is (s/valid? ::a/node (analyze ns-env 1)))
(is (s/valid? ::a/node (analyze ns-env 1.2)))
(is (s/valid? ::a/node (analyze ns-env true)))
(is (s/valid? ::a/node (analyze ns-env "foo")))
(let [node (analyze ns-env [])]
(is (= :vector (:op node)))
(is (s/valid? ::a/node node)))
(is (s/valid? ::a/node (analyze ns-env [1 2 3])))
(is (s/valid? ::a/node (analyze ns-env {})))
(let [node (analyze ns-env {1 2 3 4})]
(is (= :map (:op node)))
(is (s/valid? ::a/node node)))
(is (s/valid? ::a/node (analyze ns-env #{})))
(let [node (analyze ns-env #{1 2 3})]
(is (= :set (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-def
(let [node (no-warn (analyze ns-env '(def x)))]
(is (= :def (:op node)))
(is (s/valid? ::a/node node)))
(is (s/valid? ::a/node (analyze ns-env '(def x 1))))
(is (s/valid? ::a/node (analyze ns-env '(def x (fn [])))))
(is (s/valid? ::a/node (analyze ns-env '(def x (fn [y] y))))))

(deftest test-defn
(is (s/valid? ::a/node (analyze ns-env '(defn x []))))
(is (s/valid? ::a/node (analyze ns-env '(defn x [] 1))))
(is (s/valid? ::a/node (analyze ns-env '(defn x [y] y)))))

(deftest test-defrecord
(let [node (no-warn (analyze ns-env '(defrecord A [])))
body (:body node)]
(is (= :defrecord (-> body :statements first :ret :op)))
(is (s/valid? ::a/node node))))

(deftest test-deftype
(let [node (no-warn (analyze ns-env '(deftype A [])))]
(is (= :deftype (-> node :statements first :op)))
(is (s/valid? ::a/node node))))

(deftest test-do
(let [node (analyze ns-env '(do))]
(is (= :do (:op node)))
(is (s/valid? ::a/node node)))
(is (s/valid? ::a/node (analyze ns-env '(do 1))))
(is (s/valid? ::a/node (analyze ns-env '(do 1 2 3)))))

(deftest test-fn
(let [node (no-warn (analyze ns-env '(fn [])))]
(is (= :fn (:op node)))
(is (s/valid? ::a/node node)))
(is (s/valid? ::a/node (analyze ns-env '(fn [] 1))))
(is (s/valid? ::a/node (analyze ns-env '(fn [x]))))
(is (s/valid? ::a/node (analyze ns-env '(fn [x] 1)))))

(deftest test-fn-method
(let [node (analyze ns-env '(fn ([]) ([x] x)))
methods (:methods node)
fn0 (first methods)
fn1 (second methods)]
(is (= :fn-method (:op fn0)))
(is (s/valid? ::a/node fn0))
(is (= :fn-method (:op fn1)))
(is (s/valid? ::a/node fn1))))

(deftest test-host-call
(let [node (analyze ns-env '(.substring "foo" 0 1))]
(is (= :host-call (:op node)))
(is (s/valid? ::a/node node)))
(let [node (analyze ns-env '(. "foo" (substring 0 1)))]
(is (= :host-call (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-host-field
(let [node (analyze ns-env '(.-length "foo"))]
(is (= :host-field (:op node)))
(is (s/valid? ::a/node node)))
(let [node (analyze ns-env '(. "foo" -length))]
(is (= :host-field (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-if
(let [node (analyze ns-env '(if true true))]
(is (= :if (:op node)))
(is (s/valid? ::a/node node)))
(is (s/valid? ::a/node (analyze ns-env '(if true true false)))))

(deftest test-invoke
(let [node (no-warn (analyze ns-env '(count "foo")))]
(is (= :invoke (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-js
(let [node (analyze ns-env '(js* "~{}" 1))]
(is (= :js (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-js-array
(let [node (analyze ns-env
(ana-api/with-state (ana-api/empty-state)
(first (ana-api/forms-seq (StringReader. "#js [1 2 3]")))))]
(is (= :js-array (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-js-object
(let [node (analyze ns-env
(ana-api/with-state (ana-api/empty-state)
(first (ana-api/forms-seq (StringReader. "#js {:foo 1 :bar 2}")))))]
(is (= :js-object (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-js-var
(let [node (analyze ns-env 'js/String)]
(is (= :js-var (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-let
(let [node (analyze ns-env '(let []))]
(is (= :let (:op node)))
(is (s/valid? ::a/node node)))
(is (s/valid? ::a/node (analyze ns-env '(let [x 1]))))
(is (s/valid? ::a/node (analyze ns-env '(let [x 1] x)))))

(deftest test-letfn
(let [node (analyze ns-env '(letfn [(foo [] (bar)) (bar [] (foo))]))]
(is (= :letfn (:op node)))
(is (s/valid? ::a/node node))))

;; list, no longer needed, subsumed by :quote

(deftest test-local
(let [node (analyze ns-env '(fn [x] x))
fn-method (-> node :methods first)
body (-> fn-method :body)
ret (:ret body)]
(is (= :local (:op ret)))
(is (s/valid? ::a/node node))))

(deftest test-loop
(let [node (analyze ns-env '(loop []))]
(is (= :loop (:op node)))
(is (s/valid? ::a/node node)))
(let [node (analyze ns-env '(loop [x 1] x))]
(is (s/valid? ::a/node node)))
(let [node (analyze ns-env '(loop [x 1] (recur (inc x))))]
(is (s/valid? ::a/node node)))
(let [node (no-warn
(analyze ns-env
'(loop [x 100]
(if (pos? x)
(recur (dec x))
x))))]
(is (s/valid? ::a/node node))))

(deftest test-map
(let [node (no-warn (analyze ns-env '{:foo 1 :bar 2}))]
(is (= :map (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-new
(let [node (no-warn (analyze ns-env '(new String)))]
(is (= :new (:op node)))
(is (s/valid? ::a/node node)))
(is (s/valid? ::a/node (analyze ns-env '(new js/String))))
(is (s/valid? ::a/node (no-warn (analyze ns-env '(String.)))))
(is (s/valid? ::a/node (analyze ns-env '(js/String.)))))

(deftest test-no-op
(let [node (binding [ana/*unchecked-if* true]
(no-warn (analyze ns-env '(set! *unchecked-if* false))))]
(is (= :no-op (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-ns
(let [node (no-warn
(binding [ana/*cljs-ns* 'cljs.user]
(analyze ns-env '(ns foo (:require [goog.string])))))]
(is (= :ns (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-ns*
(let [node (no-warn
(binding [ana/*cljs-ns* 'cljs.user]
(analyze ns-env '(ns* (:require '[goog.string])))))]
(is (= :ns* (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-quote
(let [node (analyze ns-env ''(1 2 3))]
(is (= :quote (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-recur
(let [node (no-warn (analyze ns-env '(fn [x] (recur (inc x)))))]
(is (s/valid? ::a/node node))))

(deftest test-set
(let [node (analyze ns-env #{1 2 3})]
(is (= :set (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-set!
(let [node (no-warn (analyze ns-env '(set! x 1)))]
(is (= :set! (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-the-var
(let [node (comp-api/with-core-cljs {}
#(analyze ns-env '(var first)))]
(is (= :the-var (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-throw
(let [node (no-warn (analyze ns-env '(throw (js/Error. "foo"))))]
(is (= :throw (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-try
(let [node (no-warn (analyze ns-env '(try 1 (catch :default e) (finally))))]
(is (= :try (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-var
(let [node (no-warn (analyze ns-env '(fn [] x)))
fn-method (-> node :methods first)
body (-> fn-method :body)
ret (:ret body)]
(is (= :var (:op ret)))
(is (s/valid? ::a/node node))))

(deftest test-vector
(let [node (no-warn (analyze ns-env '[1 2]))]
(is (= :vector (:op node)))
(is (s/valid? ::a/node node))))

(deftest test-with-meta
(let [node (analyze ns-env ^{:meta 2} {:foo 1})]
(is (= :with-meta (:op node)))
(is (s/valid? ::a/node node))))

(comment

(test/run-tests)

)
Loading
Loading