Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Provide middleware point to handle *cljs-warnings*

* Add in a default warning middleware with that same behavior as the current warning handler.
* Centralize the default warning behavior, make side-effects from each invocation of warning controllable through middleware.
* fix no-warn macro, make sure undeclared-ns-form is passed to warning for the right forms
* Make analyzer default-warning-handler respect *cljs-warnings*
* Fix default-warning-handler implementation and all the callers
* Change cljs.analyzer/confirm-var-exists: Don't call warning unless :undeclared-var warning is set, AND the var hasn't been def'd
* Add some basic all-warn no-warn analyzer tests
  • Loading branch information...
commit abd44502079425f76f31c5432e7366ecd33d866a 1 parent 69f0060
Sean Grove authored October 24, 2013 swannodette committed November 04, 2013
105  src/clj/cljs/analyzer.clj
@@ -30,13 +30,50 @@
30 30
 (def ^:dynamic *constant-table* (atom {}))
31 31
 
32 32
 (def ^:dynamic *cljs-warnings*
33  
-  {:undeclared false
  33
+  {:undeclared-var false
  34
+   :undeclared-ns false
  35
+   :undeclared-ns-form true
34 36
    :redef true
35 37
    :dynamic true
36 38
    :fn-var true
37 39
    :fn-arity true
38 40
    :fn-deprecated true
39  
-   :protocol-deprecated true})
  41
+   :protocol-deprecated true
  42
+   :undeclared-protocol-symbol true
  43
+   :invalid-protocol-symbol true})
  44
+
  45
+(declare message namespaces)
  46
+
  47
+(defn ^:private default-warning-handler [warning-type env & [extra]]
  48
+  (when (warning-type *cljs-warnings*)
  49
+      (let [s (condp = warning-type
  50
+                :undeclared-var (str "WARNING: Use of undeclared Var " (:prefix extra) "/" (:suffix extra))
  51
+                :undeclared-ns (str "WARNING: No such namespace: " (:ns-sym extra))
  52
+                :dynamic (str "WARNING: " (:name extra) " not declared ^:dynamic")
  53
+                :redef   (str "WARNING: " (:sym extra) " already refers to: " (symbol (str (:ns extra)) (str (:sym extra)))
  54
+                              " being replaced by: " (symbol (str (:ns-name extra)) (str (:sym extra))))
  55
+                :fn-var (str "WARNING: " (symbol (str (:ns-name extra)) (str (:sym extra)))
  56
+                             " no longer fn, references are stale")
  57
+                :fn-arity (str "WARNING: Wrong number of args (" (:argc extra) ") passed to " (or (:ctor extra)
  58
+                                                                                                  (:name extra)))
  59
+                :fn-deprecated (str "WARNING: " (-> extra :fexpr :info :name) " is deprecated.")
  60
+                :undeclared-ns-form (str "WARNING: Referred " (:type extra) " " (:lib extra) "/" (:sym extra) " does not exist")
  61
+                :protocol-deprecated (str "WARNING: Protocol " (:protocol extra) " is deprecated")
  62
+                :undeclared-protocol-symbol (str "WARNING: Can't resolve protocol symbol " (:protocol extra))
  63
+                :invalid-protocol-symbol (str "WARNING: Symbol " (:protocol extra) " is not a protocol"))]
  64
+        (when s
  65
+          (binding [*out* *err*]
  66
+            (println (message env s)))))))
  67
+
  68
+(def ^:dynamic *cljs-warning-handlers*
  69
+  [default-warning-handler])
  70
+
  71
+(defn with-warning-handlers [handlers]
  72
+  (binding [*cljs-warning-handlers* handlers]))
  73
+
  74
+(defmacro with-warning-handlers [handlers & body]
  75
+  `(binding [*cljs-warning-handlers* ~handlers]
  76
+     ~@body))
40 77
 
41 78
 (defn munge-path [ss]
42 79
   (clojure.lang.Compiler/munge (str ss)))
@@ -79,15 +116,14 @@
79 116
   (swap! namespaces assoc key val))
80 117
 
81 118
 (defmacro no-warn [& body]
82  
-  `(binding [*cljs-warnings*
83  
-             {:undeclared false
84  
-              :redef false
85  
-              :dynamic false
86  
-              :fn-var false
87  
-              :fn-arity false
88  
-              :fn-deprecated false
89  
-              :protocol-deprecated false}]
90  
-     ~@body))
  119
+  (let [no-warnings (zipmap (keys *cljs-warnings*) (repeat false))]
  120
+    `(binding [*cljs-warnings* ~no-warnings]
  121
+       ~@body)))
  122
+
  123
+(defmacro all-warn [& body]
  124
+  (let [all-warnings (zipmap (keys *cljs-warnings*) (repeat true))]
  125
+    `(binding [*cljs-warnings* ~all-warnings]
  126
+       ~@body)))
91 127
 
92 128
 (defn get-line [x env]
93 129
   (or (-> x meta :line) (:line env)))
@@ -141,9 +177,9 @@
141 177
   (str s (when (:line env)
142 178
            (str " at line " (:line env) " " *cljs-file*))))
143 179
 
144  
-(defn warning [env s]
145  
-  (binding [*out* *err*]
146  
-    (println (message env s))))
  180
+(defn warning [warning-type env extra]
  181
+  (doseq [handler *cljs-warning-handlers*]
  182
+    (handler warning-type env extra)))
147 183
 
148 184
 (defn error
149 185
   ([env s] (error env s nil))
@@ -164,12 +200,11 @@
164 200
          (throw (error ~env (.getMessage err#) err#))))))
165 201
 
166 202
 (defn confirm-var-exists [env prefix suffix]
167  
-  (when (:undeclared *cljs-warnings*)
  203
+  (when (:undeclared-var *cljs-warnings*)
168 204
     (let [crnt-ns (-> env :ns :name)]
169 205
       (when (= prefix crnt-ns)
170 206
         (when-not (-> @namespaces crnt-ns :defs suffix)
171  
-          (warning env
172  
-            (str "WARNING: Use of undeclared Var " prefix "/" suffix)))))))
  207
+          (warning :undeclared-var env {:prefix prefix :suffix suffix}))))))
173 208
 
174 209
 (defn resolve-ns-alias [env name]
175 210
   (let [sym (symbol name)]
@@ -182,8 +217,7 @@
182 217
              ;; confirm that the library at least exists
183 218
              (nil? (io/resource (ns->relpath ns-sym)))
184 219
              (:undeclared *cljs-warnings*))
185  
-    (warning env
186  
-      (str "WARNING: No such namespace: " ns-sym))))
  220
+    (warning :undeclared-ns env {:ns-sym ns-sym})))
187 221
 
188 222
 (defn core-name?
189 223
   "Is sym visible from core in the current compilation namespace?"
@@ -257,8 +291,7 @@
257 291
           ev (resolve-existing-var env name)]
258 292
       (when (and (:dynamic *cljs-warnings*)
259 293
                  ev (not (-> ev :dynamic)))
260  
-        (warning env
261  
-          (str "WARNING: " (:name ev) " not declared ^:dynamic"))))))
  294
+        (warning :dynamic env {:ev ev})))))
262 295
 
263 296
 (declare analyze analyze-symbol analyze-seq)
264 297
 
@@ -359,9 +392,7 @@
359 392
                       (get-in @namespaces [ns-name :uses sym]))
360 393
                 (let [ev (resolve-existing-var (dissoc env :locals) sym)]
361 394
                   (when (:redef *cljs-warnings*)
362  
-                    (warning env
363  
-                      (str "WARNING: " sym " already refers to: " (symbol (str (:ns ev)) (str sym))
364  
-                           " being replaced by: " (symbol (str ns-name) (str sym)))))
  395
+                    (warning :redef env {:ev ev :sym sym :ns-name ns-name}))
365 396
                   (swap! namespaces update-in [ns-name :excludes] conj sym)
366 397
                   (update-in env [:ns :excludes] conj sym))
367 398
                 env)
@@ -382,9 +413,7 @@
382 413
         (when (and (:fn-var *cljs-warnings*)
383 414
                    (not (-> sym meta :declared))
384 415
                    (and (:fn-var v) (not fn-var?)))
385  
-          (warning env
386  
-            (str "WARNING: " (symbol (str ns-name) (str sym))
387  
-                 " no longer fn, references are stale"))))
  416
+          (warning :fn-var env {:ns-name ns-name :sym sym})))
388 417
       (swap! namespaces assoc-in [ns-name :defs sym]
389 418
                  (merge 
390 419
                    {:name name}
@@ -636,9 +665,8 @@
636 665
          argexprs (vec (map #(analyze enve %) args))
637 666
          known-num-fields (:num-fields (resolve-existing-var env ctor))
638 667
          argc (count args)]
639  
-     (when (and known-num-fields (not= known-num-fields argc))
640  
-       (warning env
641  
-         (str "WARNING: Wrong number of args (" argc ") passed to " ctor)))
  668
+     (when (and known-num-fields (not= known-num-fields argc) (:fn-arity *cljs-warnings*))
  669
+       (warning :fn-arity env {:argc argc :ctor ctor}))
642 670
 
643 671
      {:env env :op :new :form form :ctor ctorexpr :args argexprs
644 672
       :children (into [ctorexpr] argexprs)})))
@@ -694,17 +722,15 @@
694 722
 
695 723
 (defn check-uses [uses env]
696 724
   (doseq [[sym lib] uses]
697  
-    (when (and (:undeclared *cljs-warnings*)
  725
+    (when (and (:undeclared-ns *cljs-warnings*)
698 726
                (= (get-in @namespaces [lib :defs sym] ::not-found) ::not-found))
699  
-      (warning env
700  
-        (str "WARNING: Referred var " lib "/" sym " does not exist")))))
  727
+      (warning :undeclared-ns-form env {:type :var :lib lib :sym sym}))))
701 728
 
702 729
 (defn check-use-macros [use-macros env]
703 730
   (doseq [[sym lib] use-macros]
704  
-    (when (and (:undeclared *cljs-warnings*)
  731
+    (when (and (:undeclared-ns *cljs-warnings*)
705 732
                (nil? (.findInternedVar ^clojure.lang.Namespace (find-ns lib) sym)))
706  
-      (warning env
707  
-        (str "WARNING: Referred macro " lib "/" sym " does not exist")))))
  733
+      (warning :undeclared-ns env {:type :macro :lib lib :sym sym}))))
708 734
 
709 735
 (defmethod parse 'ns
710 736
   [_ env [_ name & args :as form] _]
@@ -958,12 +984,11 @@
958 984
          (when (and (not (some #{argc} (map count method-params)))
959 985
                     (or (not variadic)
960 986
                         (and variadic (< argc max-fixed-arity))))
961  
-           (warning env
962  
-             (str "WARNING: Wrong number of args (" argc ") passed to " name)))))
  987
+           (warning :fn-arity env {:name name
  988
+                                   :argc argc}))))
963 989
      (if (and (:fn-deprecated *cljs-warnings*) (-> fexpr :info :deprecated)
964 990
               (not (-> form meta :deprecation-nowarn)))
965  
-       (warning env
966  
-         (str "WARNING: " (-> fexpr :info :name) " is deprecated.")))
  991
+       (warning :fn-deprecated env {:fexpr fexpr}))
967 992
      {:env env :op :invoke :form form :f fexpr :args argexprs
968 993
       :tag (or (-> fexpr :info :tag) (-> form meta :tag)) :children (into [fexpr] argexprs)})))
969 994
 
6  src/clj/cljs/closure.clj
@@ -973,7 +973,11 @@
973 973
                     (:optimize-constants opts)
974 974
                     ana/*track-constants*)
975 975
                 ana/*cljs-warnings*
976  
-                (assoc ana/*cljs-warnings* :undeclared (true? (opts :warnings)))]
  976
+                (let [enabled? (true? (opts :warnings))]
  977
+                  (merge ana/*cljs-warnings*
  978
+                         {:undeclared-var enabled?
  979
+                          :undeclared-ns enabled?
  980
+                          :undeclared-ns-form enabled?}))]
977 981
         (let [compiled (-compile source all-opts)
978 982
               const-table (when ana/*track-constants*
979 983
                             (comp/emit-constants-table-to-file @ana/*constant-table*
9  src/clj/cljs/core.clj
@@ -606,21 +606,18 @@
606 606
     (if-let [var (cljs.analyzer/resolve-existing-var (dissoc env :locals) p)]
607 607
       (do
608 608
         (when-not (:protocol-symbol var)
609  
-          (cljs.analyzer/warning env
610  
-            (core/str "WARNING: Symbol " p " is not a protocol")))
  609
+          (cljs.analyzer/warning :invalid-protocol-symbol env {:protocol p}))
611 610
         (when (core/and (:protocol-deprecated cljs.analyzer/*cljs-warnings*)
612 611
                 (-> var :deprecated)
613 612
                 (not (-> p meta :deprecation-nowarn)))
614  
-          (cljs.analyzer/warning env
615  
-            (core/str "WARNING: Protocol " p " is deprecated")))
  613
+          (cljs.analyzer/warning :protocol-deprecated env {:protocol p}))
616 614
         (when (:protocol-symbol var)
617 615
           (swap! cljs.analyzer/namespaces
618 616
             (fn [ns]
619 617
               (update-in ns [(:ns var) :defs (symbol (name p)) :impls]
620 618
                 conj type)))))
621 619
       (when (:undeclared cljs.analyzer/*cljs-warnings*)
622  
-        (cljs.analyzer/warning env
623  
-          (core/str "WARNING: Can't resolve protocol symbol " p))))))
  620
+        (cljs.analyzer/warning :undeclared-protocol-symbol env {:protocol p})))))
624 621
 
625 622
 (defn resolve-var [env sym]
626 623
   (let [ret (-> (dissoc env :locals)
4  src/clj/cljs/repl.clj
@@ -157,7 +157,9 @@
157 157
   (binding [ana/*cljs-ns* 'cljs.user
158 158
             *cljs-verbose* verbose
159 159
             ana/*cljs-warnings* (assoc ana/*cljs-warnings*
160  
-                                  :undeclared warn-on-undeclared)
  160
+                                  :undeclared-var warn-on-undeclared
  161
+                                  :undeclared-ns warn-on-undeclared
  162
+                                  :undeclared-ns-form warn-on-undeclared)
161 163
             ana/*cljs-static-fns* static-fns]
162 164
     (when analyze-path
163 165
       (analyze-source analyze-path))
31  test/clj/cljs/analyzer_tests.clj
... ...
@@ -1,2 +1,33 @@
1 1
 (ns cljs.analyzer-tests
  2
+  (:require [cljs.analyzer :as a])
2 3
   (:use clojure.test))
  4
+
  5
+;;******************************************************************************
  6
+;;  cljs-warnings tests
  7
+;;******************************************************************************
  8
+
  9
+(defn make-counter []
  10
+  (let [counter (atom 0)]
  11
+    {:counter counter
  12
+     :f (fn [& args]
  13
+          (swap! counter inc))}))
  14
+
  15
+(def warning-forms
  16
+  {:undeclared-var (let [v (gensym)] `(~v 1 2 3))
  17
+   :fn-arity '(do (defn x [a b] (+ a b))
  18
+                  (x 1 2 3 4))})
  19
+
  20
+(defn warn-count [form]
  21
+  (let [{:keys [counter f]} (make-counter)
  22
+        tracker (fn [warning-type env & [extra]]
  23
+                  (println "Warning: " warning-type)
  24
+                  (println "\tenabled? " (warning-type a/*cljs-warnings*)))]
  25
+    (a/with-warning-handlers [f]
  26
+      (a/analyze (a/empty-env) form))
  27
+    @counter))
  28
+
  29
+(deftest no-warn
  30
+  (is (every? zero? (map (fn [[name form]] (a/no-warn (warn-count form))) warning-forms))))
  31
+
  32
+(deftest all-warn
  33
+  (is (every? #(= 1 %) (map (fn [[name form]] (a/all-warn (warn-count form))) warning-forms))))

0 notes on commit abd4450

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