From 65fb6b71b9c88f999977b6a072b9e3482a161b76 Mon Sep 17 00:00:00 2001
From: Tassilo Horn <tsdh@gnu.org>
Date: Mon, 26 Nov 2012 21:01:20 +0100
Subject: [PATCH] Don't apply macro expansion to protected forms. Also add
 letfn* as a protected form.

---
 src/main/clojure/clojure/tools/macro.clj      | 14 ++++++++------
 src/test/clojure/clojure/tools/test_macro.clj | 16 +++++++++++++++-
 2 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/src/main/clojure/clojure/tools/macro.clj b/src/main/clojure/clojure/tools/macro.clj
index 33e450d..c0e8a0c 100644
--- a/src/main/clojure/clojure/tools/macro.clj
+++ b/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
diff --git a/src/test/clojure/clojure/tools/test_macro.clj b/src/test/clojure/clojure/tools/test_macro.clj
index d702ac7..3507461 100644
--- a/src/test/clojure/clojure/tools/test_macro.clj
+++ b/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
-- 
1.8.0

