From 4b84a1a493f973ed82b9482b492f087877cc2709 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 20 Mar 2025 14:47:26 -0400 Subject: [PATCH 1/5] * fix up tests so they don't throw if no warnings --- src/test/clojure/cljs/externs_infer_tests.clj | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 8ca7ff9aa..0c6459446 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -158,9 +158,9 @@ :warnings ws})] (is (= (unsplit-lines ["Foo.Boo.prototype.wozz;"]) res)) (is (= 1 (count @ws))) - (is (string/starts-with? - (first @ws) - "Cannot resolve property wozz for inferred type js/Foo.Boo")))) + (is (some-> @ws first + (string/starts-with? + "Cannot resolve property wozz for inferred type js/Foo.Boo"))))) (deftest test-type-hint-infer-unknown-property-in-chain (let [ws (atom []) @@ -172,9 +172,9 @@ :warnings ws})] (is (= (unsplit-lines ["Foo.Boo.prototype.wozz;"]) res)) (is (= 1 (count @ws))) - (is (string/starts-with? - (first @ws) - "Cannot resolve property wozz for inferred type js/Foo.Boo")))) + (is (some-> @ws first + (string/starts-with? + "Cannot resolve property wozz for inferred type js/Foo.Boo"))))) (deftest test-type-hint-infer-unknown-method (let [ws (atom []) @@ -185,9 +185,9 @@ :warnings ws})] (is (= (unsplit-lines ["Foo.prototype.gozMethod;"]) res)) (is (= 1 (count @ws))) - (is (string/starts-with? - (first @ws) - "Cannot resolve property gozMethod for inferred type js/Foo")))) + (is (some-> @ws first + (string/starts-with? + "Cannot resolve property gozMethod for inferred type js/Foo"))))) (deftest test-infer-unknown-method-from-externs (let [ws (atom []) @@ -197,9 +197,9 @@ :warnings ws})] (is (= (unsplit-lines ["Foo.prototype.gozMethod;"]) res)) (is (= 1 (count @ws))) - (is (string/starts-with? - (first @ws) - "Cannot resolve property gozMethod for inferred type js/Foo")))) + (is (some-> @ws first + (string/starts-with? + "Cannot resolve property gozMethod for inferred type js/Foo"))))) (deftest test-infer-js-require (let [ws (atom []) @@ -211,9 +211,9 @@ :warnings ws})] (is (= (unsplit-lines ["var require;" "Object.Component;"]) res)) (is (= 1 (count @ws))) - (is (string/starts-with? - (first @ws) - "Adding extern to Object for property Component")))) + (is (some-> @ws first + (string/starts-with? + "Adding extern to Object for property Component"))))) (deftest test-set-warn-on-infer (let [ws (atom []) @@ -227,7 +227,9 @@ :warn false :with-core? true})] (is (= 1 (count @ws))) - (is (string/starts-with? (first @ws) "Cannot infer target type")))) + (is (some-> @ws first + (string/starts-with? + "Cannot infer target type"))))) (deftest test-cljs-1970-infer-with-cljs-literals (let [ws (atom []) From fd783d6e563d32b7b04c303f91a32e5c8a8ec117 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 20 Mar 2025 15:28:23 -0400 Subject: [PATCH 2/5] * (wip) externs-var-info, which could be used by both has-extern? and js-tag * test are failing because - we have a bad hack that assumes we have an instance - extern-var-info doesn't tell us what the *resolved* prefix is --- src/main/clojure/cljs/analyzer.cljc | 29 +++++++++++-------- src/test/clojure/cljs/externs_infer_tests.clj | 25 ++++++++++++++++ 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 8c61c4586..47f451967 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1030,7 +1030,7 @@ boolean Boolean symbol Symbol}) -(defn has-extern?* +(defn extern-var-info ([pre externs] (let [pre (if-some [me (find (get-in externs '[Window prototype]) @@ -1039,25 +1039,29 @@ (into [tag 'prototype] (next pre)) pre) pre)] - (has-extern?* pre externs externs))) - ([pre externs top] + (extern-var-info pre externs externs nil))) + ([pre externs top info] (cond - (empty? pre) true + (empty? pre) info :else (let [x (first pre) me (find externs x)] (cond - (not me) false + (not me) nil :else (let [[x' externs'] me - xmeta (meta x')] - (if (and (= 'Function (:tag xmeta)) (:ctor xmeta)) - (or (has-extern?* (into '[prototype] (next pre)) externs' top) - (has-extern?* (next pre) externs' top) + info' (meta x')] + (if (and (= 'Function (:tag info')) (:ctor info')) + (or (extern-var-info (into '[prototype] (next pre)) externs' top nil) + (extern-var-info (next pre) externs' top info') ;; check base type if it exists - (when-let [super (:super xmeta)] - (has-extern?* (into [super] (next pre)) externs top))) - (recur (next pre) externs' top)))))))) + (when-let [super (:super info')] + (extern-var-info (into [super] (next pre)) externs top nil))) + (recur (next pre) externs' top info')))))))) + +(defn has-extern?* + [pre externs] + (boolean (extern-var-info pre externs))) (defn has-extern? ([pre] @@ -3569,6 +3573,7 @@ {:warn-type :target :form form :property prop})) ;; Unresolveable property on existing extern (let [[pre' pre] ((juxt butlast identity) (-> tag meta :prefix))] + (println ">>>>>" pre' pre) (when (and (has-extern? pre') (not (has-extern? pre))) (warning :infer-warning env {:warn-type :property :form form diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 0c6459446..3c4db5dfe 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -35,6 +35,25 @@ (is (true? (ana/has-extern? '[baz] externs))) (is (false? (ana/has-extern? '[Baz] externs))))) +(comment + + (def externs + (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"] + :use-only-custom-externs true}))) + + ;; working + (externs/info externs '[baz]) + (externs/info externs '[Foo gozMethod]) + + (ana/extern-var-info '[baz] externs) + (ana/extern-var-info '[Foo gozMethod] externs) + + (ana/has-extern? '[Foo] externs) + + ) + (deftest test-has-extern?-defaults (let [externs (externs/externs-map)] (is (true? (ana/has-extern? '[console] externs))) @@ -189,6 +208,12 @@ (string/starts-with? "Cannot resolve property gozMethod for inferred type js/Foo"))))) +(comment + + (clojure.test/test-vars [#'test-type-hint-infer-unknown-method]) + + ) + (deftest test-infer-unknown-method-from-externs (let [ws (atom []) res (infer-test-helper From 5e31004b0f653175d55fff95e6e9f75efb271cf7 Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 20 Mar 2025 16:35:41 -0400 Subject: [PATCH 3/5] * remove println --- src/main/clojure/cljs/analyzer.cljc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 47f451967..70dfed8da 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -3573,7 +3573,6 @@ {:warn-type :target :form form :property prop})) ;; Unresolveable property on existing extern (let [[pre' pre] ((juxt butlast identity) (-> tag meta :prefix))] - (println ">>>>>" pre' pre) (when (and (has-extern? pre') (not (has-extern? pre))) (warning :infer-warning env {:warn-type :property :form form From 82c3e07d84eb832abd5f189bc2b995370e8dac8b Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 3 Apr 2025 11:57:48 -0400 Subject: [PATCH 4/5] * wip --- src/main/clojure/cljs/analyzer.cljc | 40 +++++++++++++------ src/test/clojure/cljs/externs_infer_tests.clj | 18 +++++---- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 70dfed8da..66ecbc47f 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -982,8 +982,7 @@ (if-not (= 'js x) (with-meta 'js {:prefix (conj (->> (string/split (name x) #"\.") - (map symbol) vec) - 'prototype)}) + (map symbol) vec))}) x)) (defn ->type-set @@ -1030,7 +1029,9 @@ boolean Boolean symbol Symbol}) -(defn extern-var-info +(defn resolve-extern + "Given a foreign js property list, return a resolved js property list and the + extern var info" ([pre externs] (let [pre (if-some [me (find (get-in externs '[Window prototype]) @@ -1039,10 +1040,10 @@ (into [tag 'prototype] (next pre)) pre) pre)] - (extern-var-info pre externs externs nil))) - ([pre externs top info] + (resolve-extern pre externs externs {:resolved [] :info nil}))) + ([pre externs top ret] (cond - (empty? pre) info + (empty? pre) ret :else (let [x (first pre) me (find externs x)] @@ -1052,16 +1053,31 @@ (let [[x' externs'] me info' (meta x')] (if (and (= 'Function (:tag info')) (:ctor info')) - (or (extern-var-info (into '[prototype] (next pre)) externs' top nil) - (extern-var-info (next pre) externs' top info') - ;; check base type if it exists + (or + ;; first look for a property on the prototype + (resolve-extern (into '[prototype] (next pre)) externs' top + (-> ret + (update :resolved conj 'prototype) + (assoc :info nil))) + ;; then check for "static" property + (resolve-extern (next pre) externs' top + (-> ret + (update :resolved conj x) + (assoc :info info'))) + ;; finally check the super class if there is one (when-let [super (:super info')] - (extern-var-info (into [super] (next pre)) externs top nil))) - (recur (next pre) externs' top info')))))))) + (resolve-extern (into [super] (next pre)) externs top + (-> ret + (update :resolved conj x) + (assoc :info nil))))) + (recur (next pre) externs' top + (-> ret + (update :resolved conj x) + (assoc :info info')))))))))) (defn has-extern?* [pre externs] - (boolean (extern-var-info pre externs))) + (boolean (resolve-extern pre externs))) (defn has-extern? ([pre] diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 3c4db5dfe..ff2989774 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -23,6 +23,15 @@ "goog.isArrayLike;" "Java.type;" "Object.out;" "Object.out.println;" "Object.error;" "Object.error.println;"]) +(deftest test-resolve-extern + (let [externs + (externs/externs-map + (closure/load-externs + {:externs ["src/test/externs/test.js"] + :use-only-custom-externs true}))] + (is (some? (ana/resolve-extern '[baz] externs))) + (is (nil? (ana/resolve-extern '[Foo gozMethod] externs))))) + (deftest test-has-extern?-basic (let [externs (externs/externs-map (closure/load-externs @@ -37,19 +46,12 @@ (comment - (def externs - (externs/externs-map - (closure/load-externs - {:externs ["src/test/externs/test.js"] - :use-only-custom-externs true}))) + (clojure.test/test-vars [#'test-resolve-extern]) ;; working (externs/info externs '[baz]) (externs/info externs '[Foo gozMethod]) - (ana/extern-var-info '[baz] externs) - (ana/extern-var-info '[Foo gozMethod] externs) - (ana/has-extern? '[Foo] externs) ) From ca7b365029509ebf79658308f7f8aac70f4dcd2b Mon Sep 17 00:00:00 2001 From: davidnolen Date: Mon, 7 Apr 2025 21:25:42 -0400 Subject: [PATCH 5/5] - next problem, resolving console.log w/o the old bits --- src/main/clojure/cljs/analyzer.cljc | 23 ++++++++----------- src/test/clojure/cljs/externs_infer_tests.clj | 17 ++++++++++---- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 66ecbc47f..ec73d8c19 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1033,14 +1033,7 @@ "Given a foreign js property list, return a resolved js property list and the extern var info" ([pre externs] - (let [pre (if-some [me (find - (get-in externs '[Window prototype]) - (first pre))] - (if-some [tag (-> me first meta :tag)] - (into [tag 'prototype] (next pre)) - pre) - pre)] - (resolve-extern pre externs externs {:resolved [] :info nil}))) + (resolve-extern pre externs externs {:resolved [] :info nil})) ([pre externs top ret] (cond (empty? pre) ret @@ -1054,16 +1047,18 @@ info' (meta x')] (if (and (= 'Function (:tag info')) (:ctor info')) (or + ;; then check for "static" property + (resolve-extern (next pre) externs' top + (-> ret + (update :resolved conj x) + (assoc :info info'))) + ;; first look for a property on the prototype (resolve-extern (into '[prototype] (next pre)) externs' top - (-> ret - (update :resolved conj 'prototype) - (assoc :info nil))) - ;; then check for "static" property - (resolve-extern (next pre) externs' top (-> ret (update :resolved conj x) - (assoc :info info'))) + (assoc :info nil))) + ;; finally check the super class if there is one (when-let [super (:super info')] (resolve-extern (into [super] (next pre)) externs top diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index ff2989774..4b4f4013f 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -44,15 +44,22 @@ (is (true? (ana/has-extern? '[baz] externs))) (is (false? (ana/has-extern? '[Baz] externs))))) +(deftest test-resolve-extern + (let [externs (externs/externs-map)] + (is (= '[Number] + (-> (ana/resolve-extern '[Number] externs) :resolved))) + (is (= '[Number prototype valueOf] + (-> (ana/resolve-extern '[Number valueOf] externs) :resolved))))) + (comment - (clojure.test/test-vars [#'test-resolve-extern]) + (def externs (externs/externs-map)) - ;; working - (externs/info externs '[baz]) - (externs/info externs '[Foo gozMethod]) + ;; succeeds + (ana/resolve-extern '[console] externs) - (ana/has-extern? '[Foo] externs) + ;; this one fails + (ana/resolve-extern '[console log] externs) )