Skip to content
Browse files

Fix CLJS-615: Warning when required lib does not exist

  • Loading branch information...
1 parent dd894fd commit e16a3dae7a63d1516f30cbeb065afeef6fbfb484 @timothypratley timothypratley committed with swannodette
View
88 src/clj/cljs/analyzer.clj
@@ -32,7 +32,8 @@
(def -cljs-macros-loaded (atom false))
(def ^:dynamic *cljs-warnings*
- {:undeclared-var false
+ {:unprovided true
+ :undeclared-var false
:undeclared-ns false
:undeclared-ns-form true
:redef true
@@ -54,6 +55,10 @@
(defmulti error-message (fn [warning-type & _] warning-type))
+(defmethod error-message :unprovided
+ [warning-type info]
+ (str "Required namespace not provided for " (clojure.string/join " " (:unprovided info))))
+
(defmethod error-message :undeclared-var
[warning-type info]
(str "Use of undeclared Var " (:prefix info) "/" (:suffix info)))
@@ -136,9 +141,6 @@
(def ^:dynamic *cljs-warning-handlers*
[default-warning-handler])
-(defn with-warning-handlers [handlers]
- (binding [*cljs-warning-handlers* handlers]))
-
(defmacro with-warning-handlers [handlers & body]
`(binding [*cljs-warning-handlers* ~handlers]
~@body))
@@ -277,23 +279,21 @@
(throw (error ~env (.getMessage err#) err#))))))
(defn confirm-var-exists [env prefix suffix]
- (when (:undeclared-var *cljs-warnings*)
- (let [crnt-ns (-> env :ns :name)]
- (when (= prefix crnt-ns)
- (when-not (get-in @env/*compiler* [::namespaces crnt-ns :defs suffix])
- (warning :undeclared-var env {:prefix prefix :suffix suffix}))))))
+ (let [crnt-ns (-> env :ns :name)]
+ (when (and (= prefix crnt-ns)
+ (not (get-in @env/*compiler* [::namespaces crnt-ns :defs suffix])))
+ (warning :undeclared-var env {:prefix prefix :suffix suffix}))))
(defn resolve-ns-alias [env name]
(let [sym (symbol name)]
(get (:requires (:ns env)) sym sym)))
(defn confirm-ns [env ns-sym]
- (when (and (nil? (get '#{cljs.core goog Math} ns-sym))
+ (when (and (nil? (get '#{cljs.core goog Math goog.string} ns-sym))
(nil? (get (-> env :ns :requires) ns-sym))
;; macros may refer to namespaces never explicitly required
;; confirm that the library at least exists
- (nil? (io/resource (ns->relpath ns-sym)))
- (:undeclared-ns *cljs-warnings*))
+ (nil? (io/resource (ns->relpath ns-sym))))
(warning :undeclared-ns env {:ns-sym ns-sym})))
(defn core-name?
@@ -373,8 +373,7 @@
(doseq [name names]
(let [env (assoc env :ns (get-namespace *cljs-ns*))
ev (resolve-existing-var env name)]
- (when (and (:dynamic *cljs-warnings*)
- ev (not (-> ev :dynamic)))
+ (when (and ev (not (-> ev :dynamic)))
(warning :dynamic env {:ev ev})))))
(declare analyze analyze-symbol analyze-seq)
@@ -570,8 +569,7 @@
(core-name? env sym))
(get-in @env/*compiler* [::namespaces ns-name :uses sym]))
(let [ev (resolve-existing-var (dissoc env :locals) sym)]
- (when (:redef *cljs-warnings*)
- (warning :redef env {:ev ev :sym sym :ns-name ns-name}))
+ (warning :redef env {:ev ev :sym sym :ns-name ns-name})
(swap! env/*compiler* update-in [::namespaces ns-name :excludes] conj sym)
(update-in env [:ns :excludes] conj sym))
env)
@@ -592,8 +590,7 @@
(if (= true export-val) name export-val))
doc (or (:doc args) (-> sym meta :doc))]
(when-let [v (get-in @env/*compiler* [::namespaces ns-name :defs sym])]
- (when (and (:fn-var *cljs-warnings*)
- (not (-> sym meta :declared))
+ (when (and (not (-> sym meta :declared))
(and (:fn-var v) (not fn-var?)))
(warning :fn-var env {:ns-name ns-name :sym sym})))
(swap! env/*compiler* assoc-in [::namespaces ns-name :defs sym]
@@ -724,12 +721,11 @@
(let [variadic-methods (filter :variadic methods)
variadic-params (count (:params (first variadic-methods)))
param-counts (map (comp count :params) methods)]
- (when (and (:multiple-variadic-overloads *cljs-warnings*) (< 1 (count variadic-methods)))
+ (when (< 1 (count variadic-methods))
(warning :multiple-variadic-overloads env {:name name-var}))
- (when (and (:variadic-max-arity *cljs-warnings*)
- (not (or (zero? variadic-params) (= variadic-params (+ 1 max-fixed-arity)))))
+ (when (not (or (zero? variadic-params) (= variadic-params (+ 1 max-fixed-arity))))
(warning :variadic-max-arity env {:name name-var}))
- (when (and (:overload-arity *cljs-warnings*) (not= (distinct param-counts) param-counts))
+ (when (not= (distinct param-counts) param-counts)
(warning :overload-arity env {:name name-var})))
{:env env :op :fn :form form :name name-var :methods methods :variadic variadic
:tag 'function
@@ -891,8 +887,7 @@
argexprs (vec (map #(analyze enve %) args))
known-num-fields (:num-fields (resolve-existing-var env ctor))
argc (count args)]
- (when (and (:fn-arity *cljs-warnings*)
- (not (-> ctor meta :internal-ctor))
+ (when (and (not (-> ctor meta :internal-ctor))
known-num-fields (not= known-num-fields argc))
(warning :fn-arity env {:argc argc :ctor ctor}))
{:env env :op :new :form form :ctor ctorexpr :args argexprs
@@ -947,28 +942,36 @@
(declare analyze-file)
-(defn analyze-deps [lib deps]
+(defn locate-src [relpath]
+ (or (io/resource relpath)
+ (let [root (:root @env/*compiler*)
+ root-path (when root (.getPath ^File root))
+ f (io/file (str root-path \/ relpath))]
+ (when (and (.exists f) (.isFile f))
+ f))))
+
+(defn analyze-deps [lib deps env]
(binding [*cljs-dep-set* (vary-meta (conj *cljs-dep-set* lib) update-in [:dep-path] conj lib)]
(assert (every? #(not (contains? *cljs-dep-set* %)) deps)
(str "Circular dependency detected " (-> *cljs-dep-set* meta :dep-path)))
(doseq [dep deps]
- (when-not (contains? (::namespaces @env/*compiler*) dep)
- (let [relpath (ns->relpath dep)]
- (when (io/resource relpath)
- (no-warn
- (analyze-file relpath))))))))
+ (when-not (or (contains? (::namespaces @env/*compiler*) dep)
+ (contains? (:js-dependency-index @env/*compiler*) (name dep)))
+ (let [relpath (ns->relpath dep)
+ src (locate-src relpath)]
+ (if src
+ (analyze-file src)
+ (warning :undeclared-ns env {:ns-sym dep})))))))
(defn check-uses [uses env]
(doseq [[sym lib] uses]
- (when (and (:undeclared-ns-form *cljs-warnings*)
- (= (get-in @env/*compiler* [::namespaces lib :defs sym] ::not-found) ::not-found))
- (warning :undeclared-ns-form env {:type :var :lib lib :sym sym}))))
+ (when (= (get-in @env/*compiler* [::namespaces lib :defs sym] ::not-found) ::not-found)
+ (warning :undeclared-ns-form env {:type "var" :lib lib :sym sym}))))
(defn check-use-macros [use-macros env]
(doseq [[sym lib] use-macros]
- (when (and (:undeclared-ns *cljs-warnings*)
- (nil? (.findInternedVar ^clojure.lang.Namespace (find-ns lib) sym)))
- (warning :undeclared-ns env {:type :macro :lib lib :sym sym}))))
+ (when (nil? (.findInternedVar ^clojure.lang.Namespace (find-ns lib) sym))
+ (warning :undeclared-ns-form env {:type "macro" :lib lib :sym sym}))))
(defn parse-ns-error-msg [spec msg]
(str msg "; offending spec: " (pr-str spec)))
@@ -1125,7 +1128,7 @@
(apply merge-with merge m (map (spec-parsers k) libs)))
{} (remove (fn [[r]] (= r :refer-clojure)) args))]
(when (and *analyze-deps* (seq @deps))
- (analyze-deps name @deps))
+ (analyze-deps name @deps env))
(when (seq uses)
(check-uses uses env))
(set! *cljs-ns* name)
@@ -1266,8 +1269,7 @@
(seg (subs s (inc end)))))))))
enve (assoc env :context :expr)
argexprs (vec (map #(analyze enve %) args))]
- (when (and (-> form meta :numeric)
- (:invalid-arithmetic *cljs-warnings*))
+ (when (-> form meta :numeric)
(let [types (map #(infer-tag env %) argexprs)]
(when-not (every?
(fn [t]
@@ -1313,18 +1315,17 @@
fexpr (analyze enve f)
argexprs (vec (map #(analyze enve %) args))
argc (count args)]
- (when (and (:fn-arity *cljs-warnings*) (-> fexpr :info :fn-var))
+ (when (-> fexpr :info :fn-var)
(let [{:keys [variadic max-fixed-arity method-params name]} (:info fexpr)]
(when (and (not (some #{argc} (map count method-params)))
(or (not variadic)
(and variadic (< argc max-fixed-arity))))
(warning :fn-arity env {:name name
:argc argc}))))
- (when (and (:fn-deprecated *cljs-warnings*) (-> fexpr :info :deprecated)
+ (when (and (-> fexpr :info :deprecated)
(not (-> form meta :deprecation-nowarn)))
(warning :fn-deprecated env {:fexpr fexpr}))
- (when (and (:invoke-ctor *cljs-warnings*)
- (-> fexpr :info :type))
+ (when (-> fexpr :info :type)
(warning :invoke-ctor env {:fexpr fexpr}))
{:env env :op :invoke :form form :f fexpr :args argexprs
:children (into [fexpr] argexprs)})))
@@ -1556,6 +1557,7 @@ argument, which the reader will use in any emitted errors."
(defn analyze-file [f]
(let [res (cond
(instance? File f) f
+ (instance? java.net.URL f) f
(re-find #"^file://" f) (java.net.URL. f)
:else (io/resource f))]
(assert res (str "Can't find " f " in classpath"))
View
69 src/clj/cljs/closure.clj
@@ -622,17 +622,16 @@
the value is a list of directories containing third-party
libraries."
[opts requires]
- (let [index (js-dependency-index opts)]
- (loop [requires requires
- visited (set requires)
- deps #{}]
- (if (seq requires)
- (let [node (get index (first requires))
- new-req (remove #(contains? visited %) (:requires node))]
- (recur (into (rest requires) new-req)
- (into visited new-req)
- (conj deps node)))
- (remove nil? deps)))))
+ (loop [requires requires
+ visited (set requires)
+ deps #{}]
+ (if (seq requires)
+ (let [node (get (@env/*compiler* :js-dependency-index) (first requires))
+ new-req (remove #(contains? visited %) (:requires node))]
+ (recur (into (rest requires) new-req)
+ (into visited new-req)
+ (conj deps node)))
+ (remove nil? deps))))
(comment
;; find dependencies
@@ -657,24 +656,23 @@
Only load dependencies from the classpath."
[opts requires]
- (let [index (js-dependency-index opts)]
- (letfn [(ns->cp [s] (str (string/replace (munge s) \. \/) ".cljs"))
- (cljs-deps [coll]
- (->> coll
- (remove #(contains? index %))
- (map #(let [f (ns->cp %)] (hash-map :relative-path f :uri (io/resource f))))
- (remove #(nil? (:uri %)))))]
- (loop [required-files (cljs-deps requires)
- visited (set required-files)
- js-deps #{}]
- (if (seq required-files)
- (let [next-file (first required-files)
- js (get-compiled-cljs opts next-file)
- new-req (remove #(contains? visited %) (cljs-deps (-requires js)))]
- (recur (into (rest required-files) new-req)
- (into visited new-req)
- (conj js-deps js)))
- (remove nil? js-deps))))))
+ (letfn [(ns->cp [s] (str (string/replace (munge s) \. \/) ".cljs"))
+ (cljs-deps [coll]
+ (->> coll
+ (remove (@env/*compiler* :js-dependency-index))
+ (map #(let [f (ns->cp %)] (hash-map :relative-path f :uri (io/resource f))))
+ (remove #(nil? (:uri %)))))]
+ (loop [required-files (cljs-deps requires)
+ visited (set required-files)
+ js-deps #{}]
+ (if (seq required-files)
+ (let [next-file (first required-files)
+ js (get-compiled-cljs opts next-file)
+ new-req (remove #(contains? visited %) (cljs-deps (-requires js)))]
+ (recur (into (rest required-files) new-req)
+ (into visited new-req)
+ (conj js-deps js)))
+ (remove nil? js-deps)))))
(comment
;; only get cljs deps
@@ -692,7 +690,11 @@
[opts & inputs]
(let [requires (mapcat -requires inputs)
required-cljs (remove (set inputs) (cljs-dependencies opts requires))
- required-js (js-dependencies opts (set (concat (mapcat -requires required-cljs) requires)))]
+ required-js (js-dependencies opts (set (concat (mapcat -requires required-cljs) requires)))
+ provided (mapcat -provides (concat inputs required-cljs required-js))
+ unprovided (clojure.set/difference (set requires) (set provided) #{"constants-table"})]
+ (when (seq unprovided)
+ (ana/warning :unprovided @env/*compiler* {:unprovided (sort unprovided)}))
(cons (javascript-file nil (io/resource "goog/base.js") ["goog"] nil)
(dependency-order
(concat (map #(-> (javascript-file (:foreign %)
@@ -1126,6 +1128,8 @@
(check-source-map opts)
(check-source-map-path opts)
(swap! compiler-env assoc-in [:opts :emit-constants] emit-constants)
+ ; JavaScript dependencies are found first so that they are known when resolving ns :refer
+ (swap! compiler-env assoc :js-dependency-index (js-dependency-index opts))
(binding [ana/*cljs-static-fns*
(or (and (= (:optimizations opts) :advanced)
(not (false? (:static-fns opts))))
@@ -1133,9 +1137,10 @@
ana/*cljs-static-fns*)
*assert* (not= (:elide-asserts opts) true)
ana/*cljs-warnings*
- (let [enabled? (true? (opts :warnings))]
+ (let [enabled? (true? (opts :warnings true))]
(merge ana/*cljs-warnings*
- {:undeclared-var enabled?
+ {:unprovided enabled?
+ :undeclared-var enabled?
:undeclared-ns enabled?
:undeclared-ns-form enabled?}))]
(let [compiled (-compile source all-opts)
View
3 src/clj/cljs/compiler.clj
@@ -992,7 +992,7 @@
(if (.exists src-file)
(try
(let [{ns :ns :as ns-info} (parse-ns src-file dest-file opts)]
- (if (or (requires-compilation? src-file dest-file opts))
+ (if (requires-compilation? src-file dest-file opts)
(do (mkdirs dest-file)
(when (contains? (::ana/namespaces @env/*compiler*) ns)
(swap! env/*compiler* update-in [::ana/namespaces] dissoc ns))
@@ -1049,6 +1049,7 @@
([src-dir target-dir]
(compile-root src-dir target-dir nil))
([src-dir target-dir opts]
+ (swap! env/*compiler* assoc :root src-dir)
(let [src-dir-file (io/file src-dir)]
(loop [cljs-files (cljs-files-in src-dir-file)
output-files []]
View
5 src/clj/cljs/repl.clj
@@ -180,7 +180,8 @@
(defn repl
"Note - repl will reload core.cljs every time, even if supplied old repl-env"
- [repl-env & {:keys [analyze-path verbose warn-on-undeclared special-fns static-fns]}]
+ [repl-env & {:keys [analyze-path verbose warn-on-undeclared special-fns static-fns] :as opts
+ :or {warn-on-undeclared true}}]
(print "To quit, type: ")
(prn :cljs/quit)
(env/with-compiler-env
@@ -188,10 +189,12 @@
(binding [ana/*cljs-ns* 'cljs.user
*cljs-verbose* verbose
ana/*cljs-warnings* (assoc ana/*cljs-warnings*
+ :unprovided warn-on-undeclared
:undeclared-var warn-on-undeclared
:undeclared-ns warn-on-undeclared
:undeclared-ns-form warn-on-undeclared)
ana/*cljs-static-fns* static-fns]
+ (swap! env/*compiler* assoc :js-dependency-index (cljsc/js-dependency-index opts))
(when analyze-path
(analyze-source analyze-path))
(let [env {:context :expr :locals {}}
View
1 src/clj/cljs/repl/browser.clj
@@ -255,6 +255,7 @@
:cljs.env/compiler compiler-env
:source-map true}
opts)]
+ (swap! compiler-env assoc :js-dependency-index (cljsc/js-dependency-index opts))
(cljs.env/with-compiler-env compiler-env
(reset! preloaded-libs (set (concat (always-preload) (map str (:preloaded-libs opts)))))
(reset! loaded-libs @preloaded-libs)
View
14 test/clj/cljs/analyzer_tests.clj
@@ -8,23 +8,17 @@
;; cljs-warnings tests
;;******************************************************************************
-(defn make-counter []
- (let [counter (atom 0)]
- {:counter counter
- :f (fn [& args]
- (swap! counter inc))}))
-
(def warning-forms
{:undeclared-var (let [v (gensym)] `(~v 1 2 3))
:fn-arity '(do (defn x [a b] (+ a b))
(x 1 2 3 4))})
(defn warn-count [form]
- (let [{:keys [counter f]} (make-counter)
+ (let [counter (atom 0)
tracker (fn [warning-type env & [extra]]
- (println "Warning: " warning-type)
- (println "\tenabled? " (warning-type a/*cljs-warnings*)))]
- (a/with-warning-handlers [f]
+ (when (warning-type a/*cljs-warnings*)
+ (swap! counter inc)))]
+ (a/with-warning-handlers [tracker]
(a/analyze (a/empty-env) form))
@counter))
View
1 test/cljs/cljs/core_test.cljs
@@ -1957,6 +1957,7 @@
(set (map identity (into [] (range 32))))))
;; CLJS-580
+ (def foo580)
(def foo580 {:a (fn []) :b (fn [] (foo580 :a))})
(assert (nil? (((:b foo580)))))
View
0 test/cljs/cljs/top-level.cljs → test/cljs/cljs/top_level.cljs
File renamed without changes.

0 comments on commit e16a3da

Please sign in to comment.
Something went wrong with that request. Please try again.