cljs-dev

ClojureScript compiler & std lib dev, https://clojurescript.org/community/dev
cfleming 2020-09-02T00:17:13.000100Z

Following on from my earlier analyser problem, I’ve now updated my code to use some code cribbed from cljdoc, which does something very similar to what I was doing. It works fine for simple cases, but I can’t get it to work for a test case I’m working on. I’m trying to use a cljc file to define a macro which will generate some vars in cljs which Cursive can’t see via static analysis. However when I run the analyser over it, what I get back looks more like the clj side of things:

{:name stub-test,
 :publics ({:name normal-def, :type :var}
           {:name normal-defn, :arglists ([]), :type :var}
           {:name hidden-defmacro2, :arglists ([]), :doc "Doc", :type :macro}
           {:name gen-clj-stuff, :arglists ([]), :type :macro}
           {:name NormalProtocol, :type :protocol, :members ({:name normal-method, :arglists ([this]), :type :var})}
           {:name hidden-defmacro5, :arglists ([foo__2740__auto__]), :type :macro}
           {:name gen-stuff, :arglists ([]), :type :macro}
           {:name hidden-defmacro6, :arglists ([foo__2740__auto__] [foo__2740__auto__ bar__2741__auto__]), :type :macro}
           {:name normal-multimethod, :type :multimethod}
           {:name hidden-defmacro4, :arglists ([]), :doc "Moar doc", :type :macro}
           {:name normal-method, :arglists ([this]), :type :var}
           {:name hidden-defmacro3, :arglists ([]), :type :macro}
           {:name hidden-defmacro, :arglists ([]), :type :macro}
           {:name normal-macro, :arglists ([]), :type :macro})}
To me, it looks like the analyser is taking the :clj branches in the reader conditionals since the hidden-defmacro* forms are found but e.g. the hidden-defn* ones are not. Am I missing something? The code I’m using to analyse is straightforward:
(defn- analyze-file [js-dependencies file]
  (let [state (cljs.env/default-compiler-env)
        faked-js-deps (fake-js-deps js-dependencies)]
    (swap! state update :js-dependency-index #(merge faked-js-deps %))
    (ana/no-warn
      ;; The 'with-core-cljs' wrapping function ensures the namespace 'cljs.core'
      ;; is available under the sub-call to 'analyze-file'.
      ;; <https://github.com/cljdoc/cljdoc/issues/261>
      (comp/with-core-cljs state nil #(ana/analyze-file state file nil)))
    state))

(defn- read-file [ns-name resource js-dependencies]
  (let [state (analyze-file js-dependencies resource)]
    (if-let [ns (ana/find-ns state ns-name)]
      (-&gt; ns
          (select-keys [:name :doc])
          (merge (-&gt; ns-name meta (select-keys [:author :deprecated :added])))
          (utils/remove-empties)
          (assoc :publics (read-publics state ns-name))))))
*Edit:* Similarly, when analysing helix.dom (https://github.com/lilactown/helix/blob/master/src/helix/dom.cljc), the analyser sees the defmacro forms in the cljs, which I did not expect.

dnolen 2020-09-02T13:15:03.001400Z

@cfleming a bit busy today will try take a look later if someone else doesn't get to it first

mfikes 2020-09-02T13:35:49.006900Z

@cfleming I took a look yesterday and didn’t make any traction. But with your last edit, it is worth pointing out that defmacro creates a function var when compiled directly by the ClojureScript compiler (as opposed to when they are compiled by the Clojure compiler). This is how the self hosted compiler can compile and make use of macros.

mfikes 2020-09-02T13:37:23.008Z

IIRC that function has meta on it or something else that marks it as a macro implementation

dnolen 2020-09-02T14:37:12.008800Z

I can't remember there's a probably a reason for this?

dnolen 2020-09-02T14:37:17.009100Z

(do
  (def x (transient {}))
  (dotimes [n 32]
    (conj! x [n :foo]))
  (println (persistent! x)))

dnolen 2020-09-02T14:37:30.009500Z

@alexmiller this only prints out 8 kv pairs

mfikes 2020-09-02T14:43:36.010100Z

You discarding the result of conj! right; that's the problem

dnolen 2020-09-02T14:44:53.010400Z

oh blerg

cfleming 2020-09-02T21:21:25.011200Z

@mfikes Oh, interesting, thanks - I didn’t know that (in fact, my knowledge of cljs macros in general is pretty limited).

cfleming 2020-09-02T21:22:11.012100Z

However I would have expected the defmacros not to have even been seen by cljs at all since they’re behind a reader conditional.

mfikes 2020-09-02T21:22:22.012400Z

The fact that compiling a macro in ClojureScript makes a function var could be thought of as bit of an implementation detail.

cfleming 2020-09-02T23:22:08.012900Z

@dnolen If you do get a chance to look at this at some point, I have a repro repo here: https://github.com/cursive-ide/cljs-analyzer-repro

dnolen 2020-09-02T23:47:35.013400Z

@cfleming the backlog is really too disjointed for me to understand the issue here

cfleming 2020-09-02T23:48:06.013700Z

@dnolen Ok, I’ll update the README on that repo

dnolen 2020-09-02T23:50:52.014400Z

there's also just way too many thing in that repro

dnolen 2020-09-02T23:51:25.015100Z

and it's not clear what the problem or goal is?

dnolen 2020-09-02T23:51:43.015600Z

@cfleming before looking at anything let's just backtrack and talk about it for a second

dnolen 2020-09-02T23:51:55.016Z

so I understand the bigger picture

cfleming 2020-09-02T23:51:57.016100Z

Sure.

dnolen 2020-09-02T23:52:45.017900Z

so you want to analyze a file and macros are appearing in the analysis when you don't expect it?

cfleming 2020-09-02T23:52:48.018Z

So this is related to the stub generation in Cursive, where I generate function stubs for vars which are dynamically generated at runtime. Cursive can’t see these via static analysis.

dnolen 2020-09-02T23:53:12.018600Z

sure I get that, but if we can remove Cursive the discussion we can just focus on the fundamental issue

cfleming 2020-09-02T23:53:16.018800Z

Ok.

dnolen 2020-09-02T23:54:06.019700Z

so is my previous statement correct?

dnolen 2020-09-02T23:54:20.020200Z

trying to analyze some file (irrelevant how it was created, generated or not)

dnolen 2020-09-02T23:54:29.020700Z

and it has macros and those are appearing in analysis?

cfleming 2020-09-02T23:55:26.021700Z

The issue is that when analysing two example files (helix.dom and my test cljc ns), I’m not getting back the results that I would expect. When I analyse this file: https://github.com/cursive-ide/cljs-analyzer-repro/blob/master/src/stub_test.cljc, the ns publics contain macro forms which I did not expect (although mfikes explained why this might be happening, to support self-hosting). It also does not contain vars which I did expect, and I don’t understand why.

dnolen 2020-09-02T23:56:48.022600Z

so let's break apart this first thing that you've said

dnolen 2020-09-02T23:57:02.023200Z

what should and should not appear because I don't understand that at all from looking at the file

dnolen 2020-09-02T23:57:16.023700Z

line 4 the comment says the follows defs shouldn't appear

cfleming 2020-09-02T23:57:17.023800Z

I understand that the analysis might contain macro vars even for cljs, as mfikes explained. But as far as I can tell they should have been completely hidden from cljs by the reader conditionals.

dnolen 2020-09-02T23:57:31.024200Z

but that I don't understand at all

dnolen 2020-09-02T23:57:36.024500Z

you're still trying to get ahead of me 🙂

dnolen 2020-09-02T23:58:04.025300Z

line 6 will definitely appear via analysis

cfleming 2020-09-02T23:58:06.025500Z

Sorry, that is my mistake - that is a comment for what I would expect to see in Cursive which I left in for the repro. It’s not relevant here. I’ll remove that.

dnolen 2020-09-02T23:58:28.026Z

great - I just want to see something that make sense wrt. ClojureScript

dnolen 2020-09-02T23:58:43.026500Z

then hopefully you can sort out the Cursive part once we have an understanding

cfleming 2020-09-02T23:58:48.026700Z

Absolutely.

dnolen 2020-09-02T23:59:41.027Z

ok this is what I think will happen

dnolen 2020-09-02T23:59:59.027300Z

4-8, only 6 will not appear