From 2826d5e217358269c92817b7fddc27f8e3f9202c Mon Sep 17 00:00:00 2001 From: David Nolen Date: Thu, 5 May 2022 13:38:36 -0400 Subject: [PATCH] CLJS-3373: Externs Inference issue with vars invoked from foreign libs Fix invoke inference to handle invokes on vars from foreign libs. If we have a :js-var we cannot know whether it is a function or property. Note this is different from the `:js-fn-var` case where a provided extern did disambiguate. In the case of `:js-var` we throw away the leading prefix since the types simply cannot be known. Add a test case based on the one provided by Timothy Pratley. --- src/main/clojure/cljs/analyzer.cljc | 30 ++++++++++++++----- src/test/clojure/cljs/externs_infer_tests.clj | 20 +++++++++++++ 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/main/clojure/cljs/analyzer.cljc b/src/main/clojure/cljs/analyzer.cljc index 3e68bf4311..02725c6fe6 100644 --- a/src/main/clojure/cljs/analyzer.cljc +++ b/src/main/clojure/cljs/analyzer.cljc @@ -1514,15 +1514,31 @@ else-tag #{else-tag})] (into then-tag else-tag)))))))) -(defn infer-invoke [env {f :fn :keys [args] :as e}] - (let [me (assoc (find-matching-method f args) :op :fn-method)] +(defn js-var? [ast] + (= :js-var (:op ast))) + +(defn js-var-fn? [fn-ast] + (js-var? (:info fn-ast))) + +(defn fn-ast->tag + [{:keys [info] :as fn-ast}] + (cond + ;; ClojureScript Fn + (:fn-var info) (:ret-tag info) + ;; Global foreign JS Fn inferred via externs + (:js-fn-var info) (:ret-tag info) + ;; Node foreign JS *var*, we cannot distinguish between properties + ;; and functions from such libs at this time, we cannot possibly + ;; know the returns so break the leading prefix (start with raw 'js tag) + (js-var-fn? fn-ast) 'js + :else (when (= 'js (:ns info)) 'js))) + +(defn infer-invoke [env {fn-ast :fn :keys [args] :as e}] + (let [me (assoc (find-matching-method fn-ast args) :op :fn-method)] (if-some [ret-tag (infer-tag env me)] ret-tag - (let [{:keys [info]} f] - (if-some [ret-tag (if (or (true? (:fn-var info)) - (true? (:js-fn-var info))) - (:ret-tag info) - (when (= 'js (:ns info)) 'js))] + (let [] + (if-some [ret-tag (fn-ast->tag fn-ast)] ret-tag impl/ANY_SYM))))) diff --git a/src/test/clojure/cljs/externs_infer_tests.clj b/src/test/clojure/cljs/externs_infer_tests.clj index 6f3286b14f..3038254bcd 100644 --- a/src/test/clojure/cljs/externs_infer_tests.clj +++ b/src/test/clojure/cljs/externs_infer_tests.clj @@ -413,6 +413,26 @@ :with-core? true}))] (is (empty? @ws)))) +(deftest test-cljs-3373 + (testing "var from foreign libraries that are invoked as fn should propagate 'js hints" + (let [ws (atom []) + res (infer-test-helper + {:js-dependency-index {"firebase" {:global-exports '{firebase Firebase}}} + :forms '[(ns foo.core + (:require [firebase :refer [getAuth]])) + (def auth + (doto (getAuth) + (.useDeviceLanguage) + (.onAuthStateChanged (fn [user]))))] + :warnings ws + :warn true + :with-core? false})] + (is (= (unsplit-lines + ["Object.getAuth;" + "Object.useDeviceLanguage;" + "Object.onAuthStateChanged;"]) + res))))) + (comment (binding [ana/*cljs-ns* ana/*cljs-ns*] (ana/no-warn