From be81a536c9be503c4be20c242dfbd5377389dd60 Mon Sep 17 00:00:00 2001 From: Brandon Bloom Date: Wed, 28 Nov 2012 15:35:41 -0800 Subject: [PATCH] CLJS-432: Include file/line in error messages --- src/clj/cljs/analyzer.clj | 102 +++++++++++++++++++++++++--------------------- src/clj/cljs/compiler.clj | 2 +- 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/clj/cljs/analyzer.clj b/src/clj/cljs/analyzer.clj index 50cce63..cf26c8b 100644 --- a/src/clj/cljs/analyzer.clj +++ b/src/clj/cljs/analyzer.clj @@ -87,11 +87,21 @@ [& args] `(.println System/err (str ~@args))) +(defn ^String message [env s] + (str s (when (:line env) + (str " at line " (:line env) " " *cljs-file*)))) + (defn warning [env s] (binding [*out* *err*] - (println - (str s (when (:line env) - (str " at line " (:line env) " " *cljs-file*)))))) + (println (message env s)))) + +(defn error [env s] + (throw (Error. (message env s)))) + +(defmacro assert-env + ([env x] `(assert-env ~env ~x "")) + ([env x msg] + `(assert ~x (message ~env ~msg)))) (defn confirm-var-exists [env prefix suffix] (when *cljs-warn-on-undeclared* @@ -254,7 +264,7 @@ body (if name (pop body) body) try (when body (analyze-block (if (or name finally) catchenv env) body))] - (when name (assert (not (namespace name)) "Can't qualify symbol in catch")) + (when name (assert-env env (not (namespace name)) "Can't qualify symbol in catch")) {:env env :op :try* :form form :try try :finally finally @@ -276,7 +286,7 @@ protocol (-> sym meta :protocol) dynamic (-> sym meta :dynamic) ns-name (-> env :ns :name)] - (assert (not (namespace sym)) "Can't def ns-qualified name") + (assert-env env (not (namespace sym)) "Can't def ns-qualified name") (let [env (if (or (and (not= ns-name 'cljs.core) (core-name? env sym)) (get-in @namespaces [ns-name :uses sym])) @@ -405,7 +415,7 @@ (defmethod parse 'letfn* [op env [_ bindings & exprs :as form] name] - (assert (and (vector? bindings) (even? (count bindings))) "bindings must be vector of even number of elements") + (assert-env env (and (vector? bindings) (even? (count bindings))) "bindings must be vector of even number of elements") (let [n->fexpr (into {} (map (juxt first second) (partition 2 bindings))) names (keys n->fexpr) context (:context env) @@ -436,7 +446,7 @@ (defn analyze-let [encl-env [_ bindings & exprs :as form] is-loop] - (assert (and (vector? bindings) (even? (count bindings))) "bindings must be vector of even number of elements") + (assert-env encl-env (and (vector? bindings) (even? (count bindings))) "bindings must be vector of even number of elements") (let [context (:context encl-env) [bes env] (disallowing-recur @@ -445,7 +455,7 @@ bindings (seq (partition 2 bindings))] (if-let [[name init] (first bindings)] (do - (assert (not (or (namespace name) (.contains (str name) "."))) (str "Invalid local name: " name)) + (assert-env encl-env (not (or (namespace name) (.contains (str name) "."))) (str "Invalid local name: " name)) (let [init-expr (analyze env init) be {:name name :init init-expr @@ -490,8 +500,8 @@ (let [context (:context env) frame (first *recur-frames*) exprs (disallowing-recur (vec (map #(analyze (assoc env :context :expr) %) exprs)))] - (assert frame "Can't recur here") - (assert (= (count exprs) (count (:params frame))) "recur argument count mismatch") + (assert-env env frame "Can't recur here") + (assert-env env (= (count exprs) (count (:params frame))) "recur argument count mismatch") (reset! (:flag frame) true) (assoc {:env env :op :recur :form form} :frame frame @@ -504,7 +514,7 @@ (defmethod parse 'new [_ env [_ ctor & args :as form] _] - (assert (symbol? ctor) "First arg to new must be a symbol") + (assert-env env (symbol? ctor) "First arg to new must be a symbol") (disallowing-recur (let [enve (assoc env :context :expr) ctorexpr (analyze enve ctor) @@ -536,10 +546,10 @@ (symbol? target) (do (let [local (-> env :locals target)] - (assert (or (nil? local) - (and (:field local) - (:mutable local))) - "Can't set! local var or non-mutable field")) + (assert-env env (or (nil? local) + (and (:field local) + (:mutable local))) + "Can't set! local var or non-mutable field")) (analyze-symbol enve target)) :else @@ -548,7 +558,7 @@ (when (:field targetexpr) targetexpr)))) valexpr (analyze enve val)] - (assert targetexpr "set! target must be a field or a symbol naming a var") + (assert-env env targetexpr "set! target must be a field or a symbol naming a var") (cond (= targetexpr ::set-unchecked-if) {:env env :op :no-op} :else {:env env :op :set! :form form :target targetexpr :val valexpr @@ -577,8 +587,8 @@ (reduce (fn [s [k exclude xs]] (if (= k :refer-clojure) (do - (assert (= exclude :exclude) "Only [:refer-clojure :exclude (names)] form supported") - (assert (not (seq s)) "Only one :refer-clojure form is allowed per namespace definition") + (assert-env env (= exclude :exclude) "Only [:refer-clojure :exclude (names)] form supported") + (assert-env env (not (seq s)) "Only one :refer-clojure form is allowed per namespace definition") (into s xs)) s)) #{} args) @@ -586,40 +596,40 @@ valid-forms (atom #{:use :use-macros :require :require-macros :import}) error-msg (fn [spec msg] (str msg "; offending spec: " (pr-str spec))) parse-require-spec (fn parse-require-spec [macros? spec] - (assert (or (symbol? spec) (vector? spec)) - (error-msg spec "Only [lib.ns & options] and lib.ns specs supported in :require / :require-macros")) + (assert-env env (or (symbol? spec) (vector? spec)) + (error-msg spec "Only [lib.ns & options] and lib.ns specs supported in :require / :require-macros")) (when (vector? spec) - (assert (symbol? (first spec)) - (error-msg spec "Library name must be specified as a symbol in :require / :require-macros")) - (assert (odd? (count spec)) - (error-msg spec "Only :as alias and :refer (names) options supported in :require")) - (assert (every? #{:as :refer} (map first (partition 2 (next spec)))) - (error-msg spec "Only :as and :refer options supported in :require / :require-macros")) - (assert (let [fs (frequencies (next spec))] - (and (<= (fs :as 0) 1) - (<= (fs :refer 0) 1))) - (error-msg spec "Each of :as and :refer options may only be specified once in :require / :require-macros"))) + (assert-env env (symbol? (first spec)) + (error-msg spec "Library name must be specified as a symbol in :require / :require-macros")) + (assert-env env (odd? (count spec)) + (error-msg spec "Only :as alias and :refer (names) options supported in :require")) + (assert-env env (every? #{:as :refer} (map first (partition 2 (next spec)))) + (error-msg spec "Only :as and :refer options supported in :require / :require-macros")) + (assert-env env (let [fs (frequencies (next spec))] + (and (<= (fs :as 0) 1) + (<= (fs :refer 0) 1))) + (error-msg spec "Each of :as and :refer options may only be specified once in :require / :require-macros"))) (if (symbol? spec) (recur macros? [spec]) (let [[lib & opts] spec {alias :as referred :refer :or {alias lib}} (apply hash-map opts) [rk uk] (if macros? [:require-macros :use-macros] [:require :use])] - (assert (or (symbol? alias) (nil? alias)) - (error-msg spec ":as must be followed by a symbol in :require / :require-macros")) - (assert (or (and (sequential? referred) (every? symbol? referred)) - (nil? referred)) + (assert-env env (or (symbol? alias) (nil? alias)) + (error-msg spec ":as must be followed by a symbol in :require / :require-macros")) + (assert-env env (or (and (sequential? referred) (every? symbol? referred)) + (nil? referred)) (error-msg spec ":refer must be followed by a sequence of symbols in :require / :require-macros")) (when-not macros? (swap! deps conj lib)) (merge (when alias {rk {alias lib}}) (when referred {uk (apply hash-map (interleave referred (repeat lib)))}))))) use->require (fn use->require [[lib kw referred :as spec]] - (assert (and (symbol? lib) (= :only kw) (sequential? referred) (every? symbol? referred)) - (error-msg spec "Only [lib.ns :only (names)] specs supported in :use / :use-macros")) + (assert-env env (and (symbol? lib) (= :only kw) (sequential? referred) (every? symbol? referred)) + (error-msg spec "Only [lib.ns :only (names)] specs supported in :use / :use-macros")) [lib :refer referred]) parse-import-spec (fn parse-import-spec [spec] - (assert (and (symbol? spec) (nil? (namespace spec))) - (error-msg spec "Only lib.Ctor specs supported in :import")) + (assert-env env (and (symbol? spec) (nil? (namespace spec))) + (error-msg spec "Only lib.Ctor specs supported in :import")) (swap! deps conj spec) (let [ctor-sym (symbol (last (string/split (str spec) #"\.")))] {:import {ctor-sym spec} @@ -631,10 +641,10 @@ :import parse-import-spec} {uses :use requires :require uses-macros :use-macros requires-macros :require-macros imports :import :as params} (reduce (fn [m [k & libs]] - (assert (#{:use :use-macros :require :require-macros :import} k) - "Only :refer-clojure, :require, :require-macros, :use and :use-macros libspecs supported") - (assert (@valid-forms k) - (str "Only one " k " form is allowed per namespace definition")) + (assert-env env (#{:use :use-macros :require :require-macros :import} k) + "Only :refer-clojure, :require, :require-macros, :use and :use-macros libspecs supported") + (assert-env env (@valid-forms k) + (str "Only one " k " form is allowed per namespace definition")) (swap! valid-forms disj k) (apply merge-with merge m (map (spec-parsers k) libs))) {} (remove (fn [[r]] (= r :refer-clojure)) args))] @@ -714,7 +724,7 @@ ;; (. o -p ) (defmethod build-dot-form [::expr ::property ::list] [[target prop args]] - (throw (Error. (str "Cannot provide arguments " args " on property access " prop)))) + (error (str "Cannot provide arguments " args " on property access " prop))) (defn- build-method-call "Builds the intermediate method call map used to reason about the parsed form during @@ -742,7 +752,7 @@ (defmethod build-dot-form :default [dot-form] - (throw (Error. (str "Unknown dot form of " (list* '. dot-form) " with classification " (classify-dot-form dot-form))))) + (error (str "Unknown dot form of " (list* '. dot-form) " with classification " (classify-dot-form dot-form)))) (defmethod parse '. [_ env [_ target & [field & member+] :as form] _] @@ -766,7 +776,7 @@ (defmethod parse 'js* [op env [_ jsform & args :as form] _] - (assert (string? jsform)) + (assert-env env (string? jsform)) (if args (disallowing-recur (let [seg (fn seg [^String s] @@ -864,7 +874,7 @@ (or (-> form meta :line) (:line env)))] (let [op (first form)] - (assert (not (nil? op)) "Can't call nil") + (assert-env env (not (nil? op)) "Can't call nil") (let [mform (macroexpand-1 env form)] (if (identical? form mform) (if (specials op) diff --git a/src/clj/cljs/compiler.clj b/src/clj/cljs/compiler.clj index 978f3fe..8104412 100644 --- a/src/clj/cljs/compiler.clj +++ b/src/clj/cljs/compiler.clj @@ -538,7 +538,7 @@ (emits "}")) (when finally (let [{:keys [statements ret]} finally] - (assert (not= :constant (:op ret)) "finally block cannot contain constant") + (ana/assert-env env (not= :constant (:op ret)) "finally block cannot contain constant") (emits "finally {") (emit-block subcontext statements ret) (emits "}"))) -- 1.8.0