Browse files

Protect symbols bound by let and letfn against macroexpansion from an…

… outer scope
  • Loading branch information...
1 parent a47685b commit 19c8197e10079f04e55d81c26884b9248762c2ca @tsdh tsdh committed with khinsen Nov 27, 2012
Showing with 23 additions and 7 deletions.
  1. +8 −6 src/main/clojure/clojure/tools/macro.clj
  2. +15 −1 src/test/clojure/clojure/tools/test_macro.clj
View
14 src/main/clojure/clojure/tools/macro.clj
@@ -69,10 +69,11 @@
(cond
(seq? form)
(let [f (first form)]
- (cond (contains? special-forms f) form
- (contains? macro-fns f) (apply (get macro-fns f) (rest form))
+ (cond (contains? special-forms f) form
+ (and (not (protected? f))
+ (contains? macro-fns f)) (apply (get macro-fns f) (rest form))
(symbol? f) (cond
- (protected? f) form
+ (protected? f) form
; macroexpand-1 fails if f names a class
(class? (ns-resolve *ns* f)) form
:else (let [exp (expand-symbol f)]
@@ -115,9 +116,9 @@
(doall (cons [s b] (expand-bindings bindings exprs))))))))
(defn- expand-with-bindings
- "Handle let* and loop* forms. The symbols defined in them are protected
- from symbol macro expansion, the definitions and the body expressions
- are expanded recursively."
+ "Handle let*, letfn* and loop* forms. The symbols defined in them are
+ protected from symbol macro expansion, the definitions and the body
+ expressions are expanded recursively."
[form]
(let [f (first form)
bindings (partition 2 (second form))
@@ -174,6 +175,7 @@
'def #(expand-args % 2)
'new #(expand-args % 2)
'let* expand-with-bindings
+ 'letfn* expand-with-bindings
'loop* expand-with-bindings
'fn* expand-fn
'deftype* expand-deftype
View
16 src/test/clojure/clojure/tools/test_macro.clj
@@ -17,7 +17,21 @@
(deftest macrolet-test
(is (= (macroexpand-1
'(macro/macrolet [(foo [form] `(~form ~form))] (foo x)))
- '(do (x x)))))
+ '(do (x x))))
+ ;; Here, foo is a let-bound (thus protected) symbol, so it must not be
+ ;; subject to macro expansion.
+ (is (= (macroexpand-1
+ '(macro/macrolet [(foo [form] `(inc ~form))]
+ (let [foo identity]
+ (foo 1))))
+ '(do (let* [foo identity] (foo 1)))))
+ ;; And the same for letfn-bound symbols.
+ (is (= (macroexpand-1
+ '(macro/macrolet [(foo [form] `(inc ~form))]
+ (letfn [(foo [x] x)]
+ (foo 1))))
+ '(do (letfn* [foo (fn* foo ([x] x))]
+ (foo 1))))))
(deftest symbol-macrolet-test
(is (= (macroexpand-1

0 comments on commit 19c8197

Please sign in to comment.