Skip to content

Commit ec508b2

Browse files
authored
Replace tools.analyzer with smaller dependency analyzer (#749)
Replace tools.analyzer with a smaller implementation based on the analyzer in hyperfiddle/rfc with support for Clojure 1.12 syntax. - Fixes #724 - Fixes #734
1 parent 2cb20f2 commit ec508b2

File tree

7 files changed

+886
-53
lines changed

7 files changed

+886
-53
lines changed

.github/workflows/main.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ jobs:
6969
- { os: macos-latest, shell: bash }
7070
- { os: ubuntu-latest, shell: bash }
7171
- { os: windows-latest, shell: powershell }
72-
72+
clojure:
73+
- 1.10.3
74+
- 1.12.0
7375
defaults:
7476
run:
7577
shell: ${{matrix.sys.shell}}
@@ -96,9 +98,11 @@ jobs:
9698
~/.gitlibs
9799
~/.deps.clj
98100
key: ${{ runner.os }}-maven-test-${{ hashFiles('deps.edn') }}
99-
100101
- name: 🧪 Run tests
101-
run: bb test:clj :kaocha/reporter '[kaocha.report/documentation]'
102+
shell: bash
103+
run: |
104+
bb test:clj :kaocha/reporter '[kaocha.report/documentation]' \
105+
:clojure '"${{ matrix.clojure }}"'
102106
103107
static-build:
104108
runs-on: ubuntu-latest

bb.edn

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@
5050
:task (run '-check {:parallel true})}
5151

5252
test:clj {:doc "Run clojure tests"
53-
:task (apply clojure "-X:test" *command-line-args*)}
53+
:task
54+
(let [{clojure-version :clojure} (cli/parse-opts *command-line-args*)]
55+
(apply clojure (cond-> "-X:test"
56+
clojure (str ":" clojure-version))
57+
*command-line-args*))}
5458

5559
playwright:version {:doc "Print used playwright version from ui_tests/yarn.json"
5660
:task (print (->> (babashka.process/shell {:out :string} "grep -E 'playwright-core \"(.*)\"' ui_tests/yarn.lock")

deps.edn

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
{:paths ["src" "resources" "bb"]
22
:deps {org.clojure/clojure {:mvn/version "1.10.3"}
33
org.clojure/java.classpath {:mvn/version "1.0.0"}
4-
org.clojure/tools.analyzer {:mvn/version "1.2.0"}
5-
org.clojure/tools.analyzer.jvm {:mvn/version "1.3.0"}
64
babashka/fs {:mvn/version "0.5.22"}
7-
borkdude/edamame {:mvn/version "1.4.24"}
5+
borkdude/edamame {:mvn/version "1.4.28"}
86
weavejester/dependency {:mvn/version "0.2.1"}
97
com.nextjournal/beholder {:mvn/version "1.0.2"}
108
org.flatland/ordered {:mvn/version "1.15.12"}
@@ -76,6 +74,9 @@
7674
;; clj -M:test -v nextjournal.clerk.analyzer-test/analyze-doc
7775
:main-opts ["-m" "cognitect.test-runner"]}
7876

77+
:1.10.3 {:extra-deps {org.clojure/clojure {:mvn/version "1.10.3"}}}
78+
:1.12.0 {:extra-deps {org.clojure/clojure {:mvn/version "1.12.0"}}}
79+
7980
:demo {:extra-paths ["demo"]
8081
:extra-deps {com.github.seancorfield/next.jdbc {:mvn/version "1.2.659"}
8182
io.github.applied-science/shapes {:git/sha "da44031cf79a649932cb502f17388db23f2b8ace"}

notebooks/clojure_1_12.clj

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
(ns clojure-1-12)
2+
3+
(String/.length "foo")
4+
5+
(map String/.length ["f" "fo" "foo"])
6+
7+
String/1
8+
9+
Integer/parseInt ;; method value
10+
11+
String/CASE_INSENSITIVE_ORDER ;; field
12+
13+
(String/new "dude") ;; constructor
14+
15+
^[String] String/new
16+
17+
(map ^[String] String/new ["dude"])
18+
19+
(map Integer/parseInt ["1" "2" "3"])

src/nextjournal/clerk/analyzer.clj

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,12 @@
66
[clojure.java.io :as io]
77
[clojure.set :as set]
88
[clojure.string :as str]
9-
[clojure.tools.analyzer :as ana]
10-
[clojure.tools.analyzer.ast :as ana-ast]
11-
[clojure.tools.analyzer.jvm :as ana-jvm]
12-
[clojure.tools.analyzer.utils :as ana-utils]
139
[multiformats.base.b58 :as b58]
1410
[multiformats.hash :as hash]
15-
[nextjournal.clerk.parser :as parser]
11+
[nextjournal.clerk.analyzer.impl :as ana :refer [analyze*]]
1612
[nextjournal.clerk.classpath :as cp]
1713
[nextjournal.clerk.config :as config]
14+
[nextjournal.clerk.parser :as parser]
1815
[nextjournal.clerk.walk :as walk]
1916
[taoensso.nippy :as nippy]
2017
[weavejester.dependency :as dep]))
@@ -63,13 +60,13 @@
6360
(comp (keep :class)
6461
(filter class?)
6562
(map ensure-symbol))
66-
(ana-ast/nodes analyzed))
63+
(ana/nodes analyzed))
6764
(into #{}
6865
(comp (filter (comp #{:const} :op))
6966
(filter (comp #{:class} :type))
7067
(keep :form)
7168
(map ensure-symbol))
72-
(ana-ast/nodes analyzed))))
69+
(ana/nodes analyzed))))
7370

7471
#_(map type (:deps (analyze '(+ 1 2))))
7572

@@ -83,40 +80,31 @@
8380

8481
#_(rewrite-defcached '(nextjournal.clerk/defcached foo :bar))
8582

86-
(defn unresolvable-symbol-handler [ns sym ast-node]
87-
ast-node)
88-
89-
(defn wrong-tag-handler [tag ast-node]
90-
ast-node)
91-
92-
(def analyzer-passes-opts
93-
(assoc ana-jvm/default-passes-opts
94-
:validate/wrong-tag-handler wrong-tag-handler
95-
:validate/unresolvable-symbol-handler unresolvable-symbol-handler))
96-
9783
(defn form->ex-data
9884
"Returns ex-data map with the form and its location info from metadata."
9985
[form]
10086
(merge (select-keys (meta form) [:line :col :clojure.core/eval-file])
10187
{:form form}))
10288

103-
(defn- analyze-form
104-
([form] (analyze-form {} form))
89+
(defn- analyze-form*
90+
([form] (analyze-form* {} form))
10591
([bindings form]
10692
(binding [config/*in-clerk* true]
10793
(try
108-
(let [old-deftype-hack ana-jvm/-deftype]
109-
;; NOTE: workaround for tools.analyzer `-deftype` + `eval` HACK, which redefines classes which doesn't work well with instance? checks
110-
(with-redefs [ana-jvm/-deftype (fn [name class-name args interfaces]
111-
(when-not (resolve class-name)
112-
(old-deftype-hack name class-name args interfaces)))]
113-
(ana-jvm/analyze form (ana-jvm/empty-env) {:bindings bindings
114-
:passes-opts analyzer-passes-opts})))
94+
(analyze* (assoc (ana/to-env bindings)
95+
:ns (ns-name *ns*)) form)
11596
(catch java.lang.AssertionError e
11697
(throw (ex-info "Failed to analyze form"
11798
(form->ex-data form)
11899
e)))))))
119100

101+
(defn analyze-form [form]
102+
(with-bindings {clojure.lang.Compiler/LOADER (clojure.lang.RT/makeClassLoader)}
103+
(binding [ana/*deps* (or ana/*deps* (atom #{}))]
104+
(-> (analyze-form* (rewrite-defcached form))
105+
(ana/resolve-syms-pass)
106+
(ana/macroexpand-pass)))))
107+
120108
(defn ^:private var->protocol [v]
121109
(or (:protocol (meta v))
122110
v))
@@ -138,14 +126,33 @@
138126

139127
(defn analyze [form]
140128
(let [!deps (atom #{})
141-
mexpander (fn [form env]
142-
(let [f (if (seq? form) (first form) form)
143-
v (ana-utils/resolve-sym f env)]
144-
(when (and (not (-> env :locals (get f))) (var? v))
145-
(swap! !deps conj v)))
146-
(ana-jvm/macroexpand-1 form env))
147-
analyzed (analyze-form {#'ana/macroexpand-1 mexpander} (rewrite-defcached form))
148-
nodes (ana-ast/nodes analyzed)
129+
analyzed (binding [ana/*deps* !deps]
130+
(analyze-form form))
131+
_ (ana/prewalk (ana/only-nodes
132+
#{:var :binding :symbol}
133+
(fn [var-node]
134+
(case (:op var-node)
135+
:var (let [var (:var var-node)]
136+
(swap! !deps conj var))
137+
:binding (when-let [t (:tag (meta (:form var-node)))]
138+
(when-let [clazz (try (resolve t)
139+
(catch Exception _ nil))]
140+
(when (class? clazz)
141+
(swap! !deps conj (.getName ^Class clazz)))))
142+
:symbol (when-not (:local? var-node)
143+
(let [form (:form var-node)]
144+
(if (qualified-symbol? form)
145+
(let [clazz-sym (symbol (namespace form))]
146+
(when-let [clazz (try (resolve clazz-sym)
147+
(catch Exception _ nil))]
148+
(when (class? clazz)
149+
(swap! !deps conj (.getName ^Class clazz)))))
150+
(when-let [clazz (try (resolve form)
151+
(catch Exception _ nil))]
152+
(when (class? clazz)
153+
(swap! !deps conj (.getName ^Class clazz))))))))
154+
var-node)) analyzed)
155+
nodes (ana/nodes analyzed)
149156
{:keys [vars declared]} (get-vars+forward-declarations nodes)
150157
vars- (set/difference vars declared)
151158
var (when (and (= 1 (count vars))

0 commit comments

Comments
 (0)