Skip to content

Commit 3d733c8

Browse files
committed
macroexpand-1: expand (ClassName. args) to (new ClassName args)
Matches JVM Clojure behavior. Fixes constructor sugar not being expanded, which broke code walkers like riddley/cloverage.
1 parent 2553876 commit 3d733c8

File tree

3 files changed

+29
-20
lines changed

3 files changed

+29
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ SCI is used in [babashka](https://github.com/babashka/babashka),
1414

1515
- Fix `read` with `nil` or `false` as eof-value throwing instead of returning the eof-value
1616
- Fix `letfn` with duplicate function names crashing with ClassCastException
17+
- `macroexpand-1` now expands `(ClassName. args)` to `(new ClassName args)`, matching JVM Clojure
1718
- `deftype` now macroexpands to `deftype*`, matching JVM Clojure, enabling code walkers like riddley
1819
- `case` now macroexpands to JVM-compatible `case*` format, enabling tools like riddley and cloverage to work with SCI
1920
- Fix `.method` on class objects (e.g. `(.getDeclaredField String "value")`) routing to static instead of instance method path

src/sci/impl/macroexpand.cljc

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,30 @@
1919
;; (contains? #{'for} op) (analyze ctx expr)
2020
(= 'clojure.core/defrecord op) expr
2121
:else
22-
(let [f (try (resolve/resolve-symbol ctx op true)
23-
(catch #?(:clj Exception :cljs :default)
24-
_ ::unresolved))]
25-
(if (kw-identical? ::unresolved f)
26-
expr
27-
(let [var? (var? f)
28-
macro-var? (and var?
29-
(vars/isMacro f))
30-
f (if macro-var? @f f)]
31-
(if (or macro-var? (macro? f))
32-
(apply f original-expr (:bindings ctx) (rest expr))
33-
(if (str/starts-with? (str op) ".")
34-
(let [target (second expr)
35-
target (if (and (symbol? target)
36-
(interop/resolve-class ctx target))
37-
(list 'clojure.core/identity target)
38-
target)]
39-
(list* '. target (symbol (subs (str op) 1)) (nnext expr)))
40-
expr))))))
22+
(let [sname (str op)]
23+
(if (and (str/ends-with? sname ".")
24+
(not (str/starts-with? sname ".")))
25+
;; ClassName. constructor sugar -> (new ClassName args...)
26+
(list* 'new (symbol (subs sname 0 (dec (count sname)))) (rest expr))
27+
(let [f (try (resolve/resolve-symbol ctx op true)
28+
(catch #?(:clj Exception :cljs :default)
29+
_ ::unresolved))]
30+
(if (kw-identical? ::unresolved f)
31+
expr
32+
(let [var? (var? f)
33+
macro-var? (and var?
34+
(vars/isMacro f))
35+
f (if macro-var? @f f)]
36+
(if (or macro-var? (macro? f))
37+
(apply f original-expr (:bindings ctx) (rest expr))
38+
(if (str/starts-with? sname ".")
39+
(let [target (second expr)
40+
target (if (and (symbol? target)
41+
(interop/resolve-class ctx target))
42+
(list 'clojure.core/identity target)
43+
target)]
44+
(list* '. target (symbol (subs sname 1)) (nnext expr)))
45+
expr))))))))
4146
expr))
4247
expr))))
4348

@@ -46,4 +51,4 @@
4651
(let [ex (macroexpand-1 ctx form)]
4752
(if (identical? ex form)
4853
form
49-
(macroexpand ctx ex))))
54+
(macroexpand ctx ex))))

test/sci/core_test.cljc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,9 @@
11221122
(eval* "(macroexpand-1 '(.getDeclaredFields String))")))
11231123
(is (= '(. "foo" length)
11241124
(eval* "(macroexpand-1 '(.length \"foo\"))")))))
1125+
#?(:clj (testing "macroexpand-1 expands ClassName. constructor sugar"
1126+
(is (= '(new String "ABC")
1127+
(eval* "(macroexpand-1 '(String. \"ABC\"))")))))
11251128
#?(:clj (testing "macroexpand-1 accepts env as first argument"
11261129
(is (= '(if 1 1 (clojure.core/cond))
11271130
(eval* "(macroexpand-1 {'a 1} '(cond 1 1))")))

0 commit comments

Comments
 (0)