Skip to content

Commit f289ffe

Browse files
committed
CLJS-1702: Warning when using private vars
1 parent 4ac13fc commit f289ffe

File tree

10 files changed

+122
-77
lines changed

10 files changed

+122
-77
lines changed

src/main/cljs/cljs/spec/alpha.cljc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ value of 'cljs.spec.alpha/*runtime-asserts*', or false if not set. You can
551551
toggle check-asserts? with (check-asserts bool)."
552552
[spec x]
553553
`(if *compile-asserts*
554-
(if *runtime-asserts*
554+
(if @#'*runtime-asserts*
555555
(assert* ~spec ~x)
556556
~x)
557557
~x))

src/main/cljs/cljs/spec/test/alpha.cljc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
(when (and (nil? (:const v))
3939
#?(:cljs (nil? (:macro v))))
4040
(swap! instrumented-vars conj (:name v))
41-
`(let [checked# (instrument-1* '~s (var ~s) ~opts)]
41+
`(let [checked# (#'instrument-1* '~s (var ~s) ~opts)]
4242
(when checked# (set! ~s checked#))
4343
'~(:name v)))))
4444

@@ -47,7 +47,7 @@
4747
(when-let [v (ana-api/resolve &env s)]
4848
(when (@instrumented-vars (:name v))
4949
(swap! instrumented-vars disj (:name v))
50-
`(let [raw# (unstrument-1* ~s (var ~s))]
50+
`(let [raw# (#'unstrument-1* '~s (var ~s))]
5151
(when raw# (set! ~s raw#))
5252
'~(:name v)))))
5353

@@ -165,8 +165,8 @@ Returns a collection of syms naming the vars unstrumented."
165165
:sym s# :spec spec#}
166166

167167
(:args spec#)
168-
(let [tcret# (quick-check f# spec# opts#)]
169-
(make-check-result s# spec# tcret#))
168+
(let [tcret# (#'quick-check f# spec# opts#)]
169+
(#'make-check-result s# spec# tcret#))
170170

171171
:default
172172
{:failure (ex-info "No :args spec" {::s/failure :no-args-spec})
@@ -189,7 +189,7 @@ Returns a collection of syms naming the vars unstrumented."
189189
(checkable-syms* nil))
190190
([opts]
191191
(reduce into #{}
192-
[(filter fn-spec-name? (keys @s/registry-ref))
192+
[(filter fn-spec-name? (keys @@#'s/registry-ref))
193193
(keys (:spec opts))])))
194194

195195
(defmacro checkable-syms
@@ -201,7 +201,7 @@ can be checked."
201201
`(let [opts# ~opts]
202202
(validate-check-opts opts#)
203203
(reduce conj #{}
204-
'[~@(filter fn-spec-name? (keys @s/registry-ref))
204+
'[~@(filter fn-spec-name? (keys @@#'s/registry-ref))
205205
~@(keys (:spec opts))]))))
206206

207207
(defmacro check

src/main/cljs/clojure/core/reducers.cljs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
(-kv-reduce coll f init)
4141
(cond
4242
(nil? coll) init
43-
(array? coll) (array-reduce coll f init)
43+
(array? coll) (#'array-reduce coll f init)
4444
:else (-reduce coll f init)))))
4545

4646
(defprotocol CollFold

src/main/clojure/cljs/analyzer.cljc

Lines changed: 87 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
(def ^:dynamic *macro-infer* true)
6363
(def ^:dynamic *passes* nil)
6464
(def ^:dynamic *file-defs* nil)
65+
(def ^:dynamic *private-var-access-nowarn* false)
6566

6667
(def constants-ns-sym
6768
"The namespace of the constants table as a symbol."
@@ -123,6 +124,7 @@
123124
{:preamble-missing true
124125
:unprovided true
125126
:undeclared-var true
127+
:private-var-access true
126128
:undeclared-ns true
127129
:undeclared-ns-form true
128130
:redef true
@@ -305,6 +307,10 @@
305307
"Use of undeclared Var ")
306308
(:prefix info) "/" (:suffix info)))
307309

310+
(defmethod error-message :private-var-access
311+
[warning-type info]
312+
(str "var: " (:sym info) " is not public"))
313+
308314
(defmethod error-message :undeclared-ns
309315
[warning-type {:keys [ns-sym js-provide] :as info}]
310316
(str "No such namespace: " ns-sym
@@ -969,20 +975,20 @@
969975
(node-module-dep? ns) :node
970976
(dep-has-global-exports? ns) :global))
971977

972-
(defmulti resolve* (fn [sym full-ns current-ns] (ns->module-type full-ns)))
978+
(defmulti resolve* (fn [env sym full-ns current-ns] (ns->module-type full-ns)))
973979

974980
(defmethod resolve* :js
975-
[sym full-ns current-ns]
981+
[env sym full-ns current-ns]
976982
{:name (symbol (str full-ns) (str (name sym)))
977983
:ns full-ns})
978984

979985
(defmethod resolve* :node
980-
[sym full-ns current-ns]
986+
[env sym full-ns current-ns]
981987
{:name (symbol (str current-ns) (str (munge-node-lib full-ns) "." (name sym)))
982988
:ns current-ns})
983989

984990
(defmethod resolve* :global
985-
[sym full-ns current-ns]
991+
[env sym full-ns current-ns]
986992
(let [pre (into '[Object] (->> (string/split (name sym) #"\.") (map symbol) vec))]
987993
(when-not (has-extern? pre)
988994
(swap! env/*compiler* update-in
@@ -991,11 +997,26 @@
991997
:ns current-ns
992998
:tag (with-meta 'js {:prefix pre})}))
993999

1000+
(def ^:private private-var-access-exceptions
1001+
"Specially-treated symbols for which we don't trigger :private-var-access warnings."
1002+
'#{cljs.core/checked-aget
1003+
cljs.core/checked-aset
1004+
cljs.core/checked-aget'
1005+
cljs.core/checked-aset'})
1006+
9941007
(defmethod resolve* :default
995-
[sym full-ns current-ns]
996-
(merge (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym)))
997-
{:name (symbol (str full-ns) (str (name sym)))
998-
:ns full-ns}))
1008+
[env sym full-ns current-ns]
1009+
(let [sym-ast (gets @env/*compiler* ::namespaces full-ns :defs (symbol (name sym)))
1010+
sym-name (symbol (str full-ns) (str (name sym)))]
1011+
(when (and (not= current-ns full-ns)
1012+
(:private sym-ast)
1013+
(not *private-var-access-nowarn*)
1014+
(not (contains? private-var-access-exceptions sym-name)))
1015+
(warning :private-var-access env
1016+
{:sym sym-name}))
1017+
(merge sym-ast
1018+
{:name sym-name
1019+
:ns full-ns})))
9991020

10001021
(defn required? [ns env]
10011022
(or (contains? (set (vals (gets env :ns :requires))) ns)
@@ -1070,7 +1091,7 @@
10701091
(when (not= current-ns full-ns)
10711092
(confirm-ns env full-ns))
10721093
(confirm env full-ns (symbol (name sym))))
1073-
(resolve* sym full-ns current-ns))
1094+
(resolve* env sym full-ns current-ns))
10741095

10751096
(dotted-symbol? sym)
10761097
(let [idx (.indexOf s ".")
@@ -1090,13 +1111,13 @@
10901111

10911112
(some? (gets @env/*compiler* ::namespaces current-ns :uses sym))
10921113
(let [full-ns (gets @env/*compiler* ::namespaces current-ns :uses sym)]
1093-
(resolve* sym full-ns current-ns))
1114+
(resolve* env sym full-ns current-ns))
10941115

10951116
(some? (gets @env/*compiler* ::namespaces current-ns :renames sym))
10961117
(let [qualified-symbol (gets @env/*compiler* ::namespaces current-ns :renames sym)
10971118
full-ns (symbol (namespace qualified-symbol))
10981119
sym (symbol (name qualified-symbol))]
1099-
(resolve* sym full-ns current-ns))
1120+
(resolve* env sym full-ns current-ns))
11001121

11011122
(some? (gets @env/*compiler* ::namespaces current-ns :imports sym))
11021123
(recur env (gets @env/*compiler* ::namespaces current-ns :imports sym) confirm)
@@ -1352,13 +1373,14 @@
13521373
[env sym]
13531374
;; we need to dissoc locals for the `(let [x 1] (def x x))` case, because we
13541375
;; want the var's AST and `resolve-var` will check locals first. - António Monteiro
1355-
(let [env (dissoc env :locals)
1356-
var (resolve-var env sym (confirm-var-exists-throw))
1357-
expr-env (assoc env :context :expr)]
1358-
(when-some [var-ns (:ns var)]
1359-
{:var (analyze expr-env sym)
1360-
:sym (analyze expr-env `(quote ~(symbol (name var-ns) (name (:name var)))))
1361-
:meta (var-meta var expr-env)})))
1376+
(binding [*private-var-access-nowarn* true]
1377+
(let [env (dissoc env :locals)
1378+
var (resolve-var env sym (confirm-var-exists-throw))
1379+
expr-env (assoc env :context :expr)]
1380+
(when-some [var-ns (:ns var)]
1381+
{:var (analyze expr-env sym)
1382+
:sym (analyze expr-env `(quote ~(symbol (name var-ns) (name (:name var)))))
1383+
:meta (var-meta var expr-env)}))))
13621384

13631385
(defmethod parse 'var
13641386
[op env [_ sym :as form] _ _]
@@ -2104,49 +2126,50 @@
21042126
[`(. ~target ~val) alt]
21052127
[target val])]
21062128
(disallowing-recur
2107-
(let [enve (assoc env :context :expr)
2108-
texpr (cond
2109-
(symbol? target)
2110-
(do
2111-
(cond
2112-
(and (= target '*unchecked-if*) ;; TODO: proper resolve
2113-
(or (true? val) (false? val)))
2114-
(set! *unchecked-if* val)
2115-
2116-
(and (= target '*unchecked-arrays*) ;; TODO: proper resolve
2117-
(or (true? val) (false? val)))
2118-
(set! *unchecked-arrays* val)
2119-
2120-
(and (= target '*warn-on-infer*)
2121-
(or (true? val) (false? val)))
2122-
(set! *cljs-warnings* (assoc *cljs-warnings* :infer-warning val)))
2123-
(when (some? (:const (resolve-var (dissoc env :locals) target)))
2124-
(throw (error env "Can't set! a constant")))
2125-
(let [local (-> env :locals target)]
2126-
(when-not (or (nil? local)
2127-
(and (:field local)
2128-
(or (:mutable local)
2129-
(:unsynchronized-mutable local)
2130-
(:volatile-mutable local))))
2131-
(throw (error env "Can't set! local var or non-mutable field"))))
2132-
(analyze-symbol enve target))
2133-
2134-
:else
2135-
(when (seq? target)
2136-
(let [texpr (analyze-seq enve target nil)]
2137-
(when (:field texpr)
2138-
texpr))))
2139-
vexpr (analyze enve val)]
2140-
(when-not texpr
2141-
(throw (error env "set! target must be a field or a symbol naming a var")))
2142-
(cond
2143-
(and (not (:def-emits-var env)) ;; non-REPL context
2144-
(some? ('#{*unchecked-if* *unchecked-array* *warn-on-infer*} target)))
2145-
{:env env :op :no-op}
2146-
2147-
:else
2148-
{:env env :op :set! :form form :target texpr :val vexpr
2149-
:children [:target :val]})))))
2129+
(binding [*private-var-access-nowarn* true]
2130+
(let [enve (assoc env :context :expr)
2131+
texpr (cond
2132+
(symbol? target)
2133+
(do
2134+
(cond
2135+
(and (= target '*unchecked-if*) ;; TODO: proper resolve
2136+
(or (true? val) (false? val)))
2137+
(set! *unchecked-if* val)
2138+
2139+
(and (= target '*unchecked-arrays*) ;; TODO: proper resolve
2140+
(or (true? val) (false? val)))
2141+
(set! *unchecked-arrays* val)
2142+
2143+
(and (= target '*warn-on-infer*)
2144+
(or (true? val) (false? val)))
2145+
(set! *cljs-warnings* (assoc *cljs-warnings* :infer-warning val)))
2146+
(when (some? (:const (resolve-var (dissoc env :locals) target)))
2147+
(throw (error env "Can't set! a constant")))
2148+
(let [local (-> env :locals target)]
2149+
(when-not (or (nil? local)
2150+
(and (:field local)
2151+
(or (:mutable local)
2152+
(:unsynchronized-mutable local)
2153+
(:volatile-mutable local))))
2154+
(throw (error env "Can't set! local var or non-mutable field"))))
2155+
(analyze-symbol enve target))
2156+
2157+
:else
2158+
(when (seq? target)
2159+
(let [texpr (analyze-seq enve target nil)]
2160+
(when (:field texpr)
2161+
texpr))))
2162+
vexpr (analyze enve val)]
2163+
(when-not texpr
2164+
(throw (error env "set! target must be a field or a symbol naming a var")))
2165+
(cond
2166+
(and (not (:def-emits-var env)) ;; non-REPL context
2167+
(some? ('#{*unchecked-if* *unchecked-array* *warn-on-infer*} target)))
2168+
{:env env :op :no-op}
2169+
2170+
:else
2171+
{:env env :op :set! :form form :target texpr :val vexpr
2172+
:children [:target :val]}))))))
21502173

21512174
#?(:clj (declare analyze-file))
21522175

@@ -3734,7 +3757,9 @@
37343757
(if (and (not (namespace sym))
37353758
(dotted-symbol? sym))
37363759
sym
3737-
(:name (resolve-var (assoc @env/*compiler* :ns (get-namespace *cljs-ns*)) sym))))
3760+
(:name (binding [*private-var-access-nowarn* true]
3761+
(resolve-var (assoc @env/*compiler* :ns (get-namespace *cljs-ns*))
3762+
sym)))))
37383763

37393764
#?(:clj
37403765
(defn forms-seq*

src/main/clojure/cljs/analyzer/api.cljc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,9 @@
148148
[env sym]
149149
{:pre [(map? env) (symbol? sym)]}
150150
(try
151-
(ana/resolve-var env sym
152-
(ana/confirm-var-exists-throw))
151+
(binding [ana/*private-var-access-nowarn* true]
152+
(ana/resolve-var env sym
153+
(ana/confirm-var-exists-throw)))
153154
(catch #?(:clj Exception :cljs :default) e
154155
(ana/resolve-macro-var env sym))))
155156

src/main/clojure/cljs/compiler.cljc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@
135135
ss (map rf (string/split ss #"\."))
136136
ss (string/join "." ss)
137137
ms #?(:clj (clojure.lang.Compiler/munge ss)
138-
:cljs (cljs.core/munge-str ss))]
138+
:cljs (#'cljs.core/munge-str ss))]
139139
(if (symbol? s)
140140
(symbol ms)
141141
ms)))))

src/main/clojure/cljs/core.cljc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,7 +1864,7 @@
18641864

18651865
'IPrintWithWriter
18661866
`(~'-pr-writer [this# writer# opts#]
1867-
(let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# pr-writer "" " " "" opts# keyval#))]
1867+
(let [pr-pair# (fn [keyval#] (pr-sequential-writer writer# @#'pr-writer "" " " "" opts# keyval#))]
18681868
(pr-sequential-writer
18691869
writer# pr-pair# ~pr-open ", " "}" opts#
18701870
(concat [~@(map #(core/list `vector (keyword %) %) base-fields)]
@@ -2689,7 +2689,7 @@
26892689
prefer-table# (atom {})
26902690
method-cache# (atom {})
26912691
cached-hierarchy# (atom {})
2692-
hierarchy# (cljs.core/get ~options :hierarchy (cljs.core/get-global-hierarchy))]
2692+
hierarchy# (cljs.core/get ~options :hierarchy (#'cljs.core/get-global-hierarchy))]
26932693
(cljs.core/MultiFn. (cljs.core/symbol ~mm-ns ~(name mm-name)) ~dispatch-fn ~default hierarchy#
26942694
method-table# prefer-table# method-cache# cached-hierarchy#))))))
26952695

src/main/clojure/cljs/tagged_literals.cljc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
(when-not (string? form)
5757
(throw (js/Error. "Instance literal expects a string for its timestamp.")))
5858
(try
59-
(reader/read-date form)
59+
(#'reader/read-date form)
6060
(catch :default e
6161
(throw (js/Error. (. e -message)))))))
6262

src/test/cljs/cljs/pprint_test.cljs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,7 @@ Usage: *hello*
761761
(doseq [row aseq]
762762
(doseq [col row]
763763
(cl-format *out* "~4D~7,vT" col column-width))
764-
(prn)))
764+
(#'prn)))
765765
(str sb)
766766
;;TODO do we need to extend StringBufferWriter to allow access to underlying StringBuffer?
767767
#_(str (:base @@(:base @@stream)))))

src/test/clojure/cljs/analyzer_tests.clj

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,25 @@
473473
(a/analyze test-env '(def foo.core/foo 43))))
474474
(is (a/analyze test-env '(def cljs.user/foo 43))))))
475475

476+
(deftest test-cljs-1702
477+
(let [ws (atom [])]
478+
(a/with-warning-handlers [(collecting-warning-handler ws)]
479+
(e/with-compiler-env test-cenv
480+
(a/analyze-form-seq
481+
'[(ns test.cljs-1702-a)
482+
(def ^:private a 3)
483+
(def ^:private b 3)
484+
(defn- test-fn-a [a] a)
485+
(defn- test-fn-b [a] b)])
486+
(a/analyze-form-seq
487+
'[(ns test.cljs-1702-b)
488+
(test.cljs-1702-a/test-fn-a 1)
489+
(#'test.cljs-1702-a/test-fn-b 1)
490+
test.cljs-1702-a/a
491+
@#'test.cljs-1702-a/b]))
492+
(is (= ["var: test.cljs-1702-a/test-fn-a is not public"
493+
"var: test.cljs-1702-a/a is not public"] @ws)))))
494+
476495
(deftest test-cljs-1763
477496
(let [parsed (a/parse-ns-excludes {} '())]
478497
(is (= parsed

0 commit comments

Comments
 (0)