Skip to content

Commit 820e6fa

Browse files
committed
CLJS-2913: improvements to exception messages and printing
1 parent a93859a commit 820e6fa

File tree

13 files changed

+346
-100
lines changed

13 files changed

+346
-100
lines changed

src/main/clojure/cljs/analyzer.cljc

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,12 @@
692692
screen location navigator history location
693693
global process require module exports)))}))
694694

695+
(defn- source-info->error-data
696+
[{:keys [file line column]}]
697+
{:clojure.error/source file
698+
:clojure.error/line line
699+
:clojure.error/column column})
700+
695701
(defn source-info
696702
([env]
697703
(when (:line env)
@@ -716,6 +722,20 @@
716722
(doseq [handler *cljs-warning-handlers*]
717723
(handler warning-type env extra)))
718724

725+
(defn- error-data
726+
([env phase]
727+
(error-data env phase nil))
728+
([env phase symbol]
729+
(merge (-> (source-info env) source-info->error-data)
730+
{:clojure.error/phase phase}
731+
(when symbol
732+
{:clojure.error/symbol symbol}))))
733+
734+
(defn- compile-syntax-error
735+
[env msg symbol]
736+
(ex-info nil (error-data env :compile-syntax-check symbol)
737+
#?(:clj (RuntimeException. ^String msg) :cljs (js/Error. msg))))
738+
719739
(defn error
720740
([env msg]
721741
(error env msg nil))
@@ -729,14 +749,20 @@
729749
[ex]
730750
(= :cljs/analysis-error (:tag (ex-data ex))))
731751

752+
(defn has-error-data?
753+
#?(:cljs {:tag boolean})
754+
[ex]
755+
(contains? (ex-data ex) :clojure.error/phase))
756+
732757
#?(:clj
733758
(defmacro wrapping-errors [env & body]
734759
`(try
735760
~@body
736761
(catch Throwable err#
737-
(if (analysis-error? err#)
738-
(throw err#)
739-
(throw (error ~env (.getMessage err#) err#)))))))
762+
(cond
763+
(has-error-data? err#) (throw err#)
764+
(analysis-error? err#) (throw (ex-info nil (error-data ~env :compilation) err#))
765+
:else (throw (ex-info nil (error-data ~env :compilation) (error ~env (.getMessage err#) err#))))))))
740766

741767
;; namespaces implicit to the inclusion of cljs.core
742768
(def implicit-nses '#{goog goog.object goog.string goog.array Math String})
@@ -1584,9 +1610,9 @@
15841610
(defmethod parse 'if
15851611
[op env [_ test then else :as form] name _]
15861612
(when (< (count form) 3)
1587-
(throw (error env "Too few arguments to if")))
1613+
(throw (compile-syntax-error env "Too few arguments to if" 'if)))
15881614
(when (> (count form) 4)
1589-
(throw (error env "Too many arguments to if")))
1615+
(throw (compile-syntax-error env "Too many arguments to if" 'if)))
15901616
(let [test-expr (disallowing-recur (analyze (assoc env :context :expr) test))
15911617
then-expr (allowing-redef (analyze (add-predicate-induced-tags env test) then))
15921618
else-expr (allowing-redef (analyze env else))]
@@ -3698,32 +3724,41 @@
36983724
(when (some? (find-ns-obj 'cljs.spec.alpha))
36993725
@cached-var))))
37003726

3727+
(defn- var->sym [var]
3728+
#?(:clj (symbol (str (.-ns ^clojure.lang.Var var)) (str (.-sym ^clojure.lang.Var var)))
3729+
:cljs (.-sym var)))
3730+
37013731
(defn- do-macroexpand-check
3702-
[form mac-var]
3732+
[env form mac-var]
37033733
(when (not (-> @env/*compiler* :options :spec-skip-macros))
37043734
(let [mchk #?(:clj (some-> (find-ns 'clojure.spec.alpha)
37053735
(ns-resolve 'macroexpand-check))
37063736
:cljs (get-macroexpand-check-var))]
37073737
(when (some? mchk)
3708-
(mchk mac-var (next form))))))
3738+
(try
3739+
(mchk mac-var (next form))
3740+
(catch #?(:clj Throwable :cljs :default) e
3741+
(throw (ex-info nil (error-data env :macro-syntax-check (var->sym mac-var)) e))))))))
37093742

37103743
(defn macroexpand-1*
37113744
[env form]
37123745
(let [op (first form)]
37133746
(if (contains? specials op)
37143747
(do
37153748
(when (= 'ns op)
3716-
(do-macroexpand-check form (get-expander 'cljs.core/ns-special-form env)))
3749+
(do-macroexpand-check env form (get-expander 'cljs.core/ns-special-form env)))
37173750
form)
37183751
;else
37193752
(if-some [mac-var (when (symbol? op) (get-expander op env))]
37203753
(#?@(:clj [binding [*ns* (create-ns *cljs-ns*)]]
37213754
:cljs [do])
3722-
(do-macroexpand-check form mac-var)
3755+
(do-macroexpand-check env form mac-var)
37233756
(let [form' (try
37243757
(apply @mac-var form env (rest form))
37253758
#?(:clj (catch ArityException e
3726-
(throw (ArityException. (- (.actual e) 2) (.name e))))))]
3759+
(throw (ArityException. (- (.actual e) 2) (.name e)))))
3760+
(catch #?(:clj Throwable :cljs :default) e
3761+
(throw (ex-info nil (error-data env :macroexpansion (var->sym mac-var)) e))))]
37273762
(if #?(:clj (seq? form') :cljs (cljs-seq? form'))
37283763
(let [sym' (first form')
37293764
sym (first form)]

src/main/clojure/cljs/analyzer/macros.clj

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@
3939
`(try
4040
~@body
4141
(catch :default err#
42-
(if (cljs.analyzer/analysis-error? err#)
43-
(throw err#)
44-
(throw (cljs.analyzer/error ~env (.-message err#) err#))))))
42+
(cond
43+
(cljs.analyzer/has-error-data? err#) (throw err#)
44+
(cljs.analyzer/analysis-error? err#) (throw (ex-info nil (cljs.analyzer/error-data ~env :compilation) err#))
45+
:else (throw (ex-info nil (cljs.analyzer/error-data ~env :compilation) (cljs.analyzer/error ~env (.getMessage err#) err#)))))))
4546

4647
(defmacro disallowing-recur [& body]
4748
`(cljs.core/binding [cljs.analyzer/*recur-frames*

src/main/clojure/cljs/closure.clj

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,17 @@
8686
_ (when (nil? ns)
8787
(throw
8888
(ex-info (str kw " symbol " sym " is not fully qualified")
89-
(merge ex-data {kw sym}))))
89+
(merge ex-data {kw sym
90+
:clojure.error/phase :compilation}))))
9091
var-ns (symbol ns)]
9192
(when (not (find-ns var-ns))
9293
(try
9394
(locking ana/load-mutex
9495
(require var-ns))
9596
(catch Throwable t
9697
(throw (ex-info (str "Cannot require namespace referred by " kw " value " sym)
97-
(merge ex-data {kw sym})
98+
(merge ex-data {kw sym
99+
:clojure.error/phase :compilation})
98100
t)))))
99101

100102
(find-var sym)))
@@ -227,7 +229,7 @@
227229
(ex-info
228230
(str "Invalid :closure-output-charset " charset " given, only "
229231
(string/join ", " (keys string->charset)) " supported ")
230-
{}))))
232+
{:clojure.error/phase :compilation}))))
231233

232234
(defn ^CompilerOptions$LanguageMode lang-key->lang-mode [key]
233235
(case (keyword (string/replace (name key) #"^es" "ecmascript"))
@@ -261,7 +263,7 @@
261263
:off AnonymousFunctionNamingPolicy/OFF
262264
:unmapped AnonymousFunctionNamingPolicy/UNMAPPED
263265
:mapped AnonymousFunctionNamingPolicy/MAPPED
264-
(throw (IllegalArgumentException. (str "Invalid :anon-fn-naming-policy value " policy " - only :off, :unmapped, :mapped permitted")))))))
266+
(throw (util/compilation-error (IllegalArgumentException. (str "Invalid :anon-fn-naming-policy value " policy " - only :off, :unmapped, :mapped permitted"))))))))
265267

266268
(when-let [lang-key (:language-in opts :ecmascript5)]
267269
(.setLanguageIn compiler-options (lang-key->lang-mode lang-key)))
@@ -369,8 +371,8 @@
369371
[{:keys [externs use-only-custom-externs target ups-externs infer-externs] :as opts}]
370372
(let [validate (fn validate [p us]
371373
(if (empty? us)
372-
(throw (IllegalArgumentException.
373-
(str "Extern " p " does not exist")))
374+
(throw (util/compilation-error (IllegalArgumentException.
375+
(str "Extern " p " does not exist"))))
374376
us))
375377
filter-cp-js (fn [paths]
376378
(for [p paths
@@ -413,7 +415,7 @@
413415
(doseq [next (seq warnings)]
414416
(println "WARNING:" (.toString ^JSError next)))
415417
(when (seq errors)
416-
(throw (Exception. "Closure compilation failed"))))))
418+
(throw (util/compilation-error (Exception. "Closure compilation failed")))))))
417419

418420
;; Protocols for IJavaScript and Compilable
419421
;; ========================================
@@ -876,10 +878,11 @@
876878
(io/resource relpath)))]
877879
{:relative-path relpath :uri js-res :ext :js}
878880
(throw
879-
(IllegalArgumentException.
880-
(str "Namespace " ns " does not exist."
881-
(when (string/includes? ns "-")
882-
" Please check that namespaces with dashes use underscores in the ClojureScript file name.")))))))))))))
881+
(util/compilation-error
882+
(IllegalArgumentException.
883+
(str "Namespace " ns " does not exist."
884+
(when (string/includes? ns "-")
885+
" Please check that namespaces with dashes use underscores in the ClojureScript file name."))))))))))))))
883886

884887
(defn cljs-dependencies
885888
"Given a list of all required namespaces, return a list of
@@ -1284,8 +1287,8 @@
12841287
(swap! used into entries)
12851288
(into ret unused))
12861289
(throw
1287-
(IllegalArgumentException.
1288-
(str "Could not find matching namespace for " entry-sym)))))
1290+
(util/compilation-error (IllegalArgumentException.
1291+
(str "Could not find matching namespace for " entry-sym))))))
12891292
[] entries)
12901293
foreign-deps (atom [])]
12911294
;; add inputs to module
@@ -1304,8 +1307,8 @@
13041307
(when (:verbose opts)
13051308
(util/debug-prn " module" name "depends on" dep))
13061309
(.addDependency js-module ^JSModule parent-module))
1307-
(throw (IllegalArgumentException.
1308-
(str "Parent module " dep " does not exist")))))
1310+
(throw (util/compilation-error (IllegalArgumentException.
1311+
(str "Parent module " dep " does not exist"))))))
13091312
(conj ret
13101313
[name (assoc module-desc
13111314
:closure-module js-module
@@ -2052,7 +2055,7 @@
20522055
(deps/-closure-lib? js)
20532056
(deps/-foreign? js)))
20542057
(catch Throwable t
2055-
(throw (Exception. (str "Could not write JavaScript " (pr-str js)))))))
2058+
(throw (util/compilation-error (Exception. (str "Could not write JavaScript " (pr-str js))))))))
20562059

20572060
(defn source-on-disk
20582061
"Ensure that the given IJavaScript exists on disk in the output directory.
@@ -2160,8 +2163,8 @@
21602163
(:url-min ijs))
21612164
(:url ijs))]
21622165
(slurp url)
2163-
(throw (IllegalArgumentException.
2164-
(str "Foreign lib " ijs " does not exist")))))]
2166+
(throw (util/compilation-error (IllegalArgumentException.
2167+
(str "Foreign lib " ijs " does not exist"))))))]
21652168
(str (string/join "\n" (map to-js-str sources)) "\n")))
21662169

21672170
(defn add-wrapper [{:keys [output-wrapper] :as opts} js]
@@ -2689,7 +2692,8 @@
26892692
(catch Throwable t
26902693
(throw (ex-info (str "Error running preprocessing function " preprocess)
26912694
{:file (:file js-module)
2692-
:preprocess preprocess}
2695+
:preprocess preprocess
2696+
:clojure.error/phase :compilation}
26932697
t)))))
26942698

26952699
:else
@@ -2772,19 +2776,22 @@
27722776
(let [new-mappings (reader/read {:eof nil :read-cond :allow} rdr)]
27732777
(when (not (map? new-mappings))
27742778
(throw (ex-info (str "Not a valid data-reader map")
2775-
{:url url})))
2779+
{:url url
2780+
:clojure.error/phase :compilation})))
27762781
(reduce
27772782
(fn [m [k v]]
27782783
(when (not (symbol? k))
27792784
(throw (ex-info (str "Invalid form in data-reader file")
27802785
{:url url
2781-
:form k})))
2786+
:form k
2787+
:clojure.error/phase :compilation})))
27822788
(when (and (contains? mappings k)
27832789
(not= (mappings k) v))
27842790
(throw (ex-info "Conflicting data-reader mapping"
27852791
{:url url
27862792
:conflict k
2787-
:mappings m})))
2793+
:mappings m
2794+
:clojure.error/phase :compilation})))
27882795
(assoc m k v))
27892796
mappings
27902797
new-mappings)))))
@@ -3241,8 +3248,8 @@
32413248
"js" (cond-> (:provides (parse-js-ns src))
32423249
(not all-provides) first)
32433250
(throw
3244-
(IllegalArgumentException.
3245-
(str "Can't create goog.require expression for " src))))]
3251+
(util/compilation-error (IllegalArgumentException.
3252+
(str "Can't create goog.require expression for " src)))))]
32463253
(if (and (not all-provides) wrap)
32473254
(if (:reload options)
32483255
(str "goog.require(\"" goog-ns "\", true);")

src/main/clojure/cljs/compiler.cljc

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,8 @@
304304
(ex-info (str "failed compiling constant: " x "; "
305305
(pr-str (type x)) " is not a valid ClojureScript constant.")
306306
{:constant x
307-
:type (type x)})))
307+
:type (type x)
308+
:clojure.error/phase :compilation})))
308309

309310
(defmethod emit-constant* nil [x] (emits "null"))
310311

@@ -1398,8 +1399,8 @@
13981399
(clojure.string/replace file-str #"\.cljc$" ".js"))
13991400

14001401
:else
1401-
(throw (IllegalArgumentException.
1402-
(str "Invalid source file extension " file-str))))))
1402+
(throw (util/compilation-error (IllegalArgumentException.
1403+
(str "Invalid source file extension " file-str)))))))
14031404

14041405
#?(:clj
14051406
(defn with-core-cljs
@@ -1702,8 +1703,8 @@
17021703
(with-core-cljs opts (fn [] (ana/analyze-file src-file opts))))
17031704
(assoc ns-info :out-file (.toString dest-file)))))
17041705
(catch Exception e
1705-
(throw (ex-info (str "failed compiling file:" src) {:file src} e))))
1706-
(throw (java.io.FileNotFoundException. (str "The file " src " does not exist.")))))))))
1706+
(throw (ex-info (str "failed compiling file:" src) {:file src :clojure.error/phase :compilation} e))))
1707+
(throw (util/compilation-error (java.io.FileNotFoundException. (str "The file " src " does not exist."))))))))))
17071708

17081709
#?(:clj
17091710
(defn cljs-files-in
@@ -1788,7 +1789,8 @@
17881789
:else (throw
17891790
(ex-info
17901791
(str "Cannot emit constant for type " (type sym))
1791-
{:error :invalid-constant-type})))
1792+
{:error :invalid-constant-type
1793+
:clojure.error/phase :compilation})))
17921794
(emits ";\n"))))
17931795

17941796
#?(:clj

src/main/clojure/cljs/js_deps.cljc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,8 @@ case."
179179
(if-let [file (get-file dep index')]
180180
(update-in index' [file] lib-spec-merge dep)
181181
(throw
182-
(Exception.
183-
(str "No :file provided for :foreign-libs spec " (pr-str dep)))))
182+
(util/compilation-error (Exception.
183+
(str "No :file provided for :foreign-libs spec " (pr-str dep))))))
184184
(assoc index' (:file dep) dep))))
185185
{} deps))
186186

0 commit comments

Comments
 (0)