branch: elpa/cider
commit efc4b34ee8830b05e2e20d83cedff730be9e4c60
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>

    [Fix #4111] Relax the overzealous macroexpansion guard
    
    cider-ensure-macro checked the special-form flag before the macro flag,
    so let, fn, loop and letfn - real macros that merely carry special-form
    documentation metadata while delegating to the *-suffixed special forms
    - were wrongly rejected. Check macro first.
    
    Non-symbol operators (auto-resolved keywords, #() and other reader
    syntax, literals) now pass the guard instead of erroring: expanding them
    is a no-op on the runtime side, but the reader still normalizes the form
    usefully, a behavior people relied on before 2.0.
---
 lisp/cider-client.el       | 19 ++++++++++++-------
 test/cider-client-tests.el | 12 ++++++++++--
 2 files changed, 22 insertions(+), 9 deletions(-)

diff --git a/lisp/cider-client.el b/lisp/cider-client.el
index f4e9a2a624..55b47b7846 100644
--- a/lisp/cider-client.el
+++ b/lisp/cider-client.el
@@ -772,19 +772,24 @@ last one being the out-of-sync case where the namespace 
is loaded but stale."
   "Signal a helpful `user-error' unless OPERATOR names a resolvable macro.
 This turns the silent \"nothing happens\" case (e.g. the namespace hasn't
 been evaluated yet) into an actionable message, distinguishing an unresolved
-symbol from a special form or an ordinary (non-macro) var."
-  (cond
-   ((not (cider--symbol-operator-p operator))
-    (user-error "`%s' is not a macro" (or operator "form")))
-   (t
+symbol from a special form or an ordinary (non-macro) var.
+
+A nil or non-symbol OPERATOR (a literal, reader syntax like \\=`::kw\\=` or
+\\=`#(...)\\=`, and so on) passes the check: expanding it is a no-op on the
+runtime side, but the reader may still normalize the form usefully."
+  (when (cider--symbol-operator-p operator)
     (let ((info (cider-var-info operator)))
       (cond
        ((null info)
         (user-error "%s" (cider-resolution-failure-message operator)))
+       ;; `let', `fn', `loop' and `letfn' carry both `:macro' and
+       ;; `:special-form' metadata - they are real macros delegating to the
+       ;; `*'-suffixed special forms - so the macro check must come first.
+       ((nrepl-dict-get info "macro") nil)
        ((nrepl-dict-get info "special-form")
         (user-error "`%s' is a special form; there's nothing to macroexpand" 
operator))
-       ((not (nrepl-dict-get info "macro"))
-        (user-error "`%s' is not a macro" operator)))))))
+       (t
+        (user-error "`%s' is not a macro" operator))))))
 
 
 ;;; Requests
diff --git a/test/cider-client-tests.el b/test/cider-client-tests.el
index dee500a084..08a2b0b114 100644
--- a/test/cider-client-tests.el
+++ b/test/cider-client-tests.el
@@ -172,15 +172,23 @@
     (spy-on 'cider-var-info :and-return-value nil)
     (spy-on 'cider-resolution-failure-message :and-return-value "nope")
     (expect (cider-ensure-macro "my.ns/foo") :to-throw 'user-error))
+  (it "passes for macros that double as special forms (let, fn, loop, letfn)"
+    ;; #4111: these carry both `:macro' and `:special-form' metadata.
+    (spy-on 'cider-var-info :and-return-value (nrepl-dict "macro" "true"
+                                                          "special-form" 
"true"))
+    (expect (cider-ensure-macro "let") :not :to-throw))
   (it "rejects special forms"
     (spy-on 'cider-var-info :and-return-value (nrepl-dict "special-form" 
"true"))
     (expect (cider-ensure-macro "if") :to-throw 'user-error))
   (it "rejects ordinary (non-macro) vars"
     (spy-on 'cider-var-info :and-return-value (nrepl-dict "arglists" 
"([coll])"))
     (expect (cider-ensure-macro "map") :to-throw 'user-error))
-  (it "rejects non-symbol operators without consulting the runtime"
+  (it "passes non-symbol operators through without consulting the runtime"
+    ;; #4111: reader syntax (::kw, #(...), literals) is expanded as a no-op
+    ;; server-side, but the reader still normalizes it usefully.
     (spy-on 'cider-var-info)
-    (expect (cider-ensure-macro ":kw") :to-throw 'user-error)
+    (expect (cider-ensure-macro ":kw") :not :to-throw)
+    (expect (cider-ensure-macro nil) :not :to-throw)
     (expect 'cider-var-info :not :to-have-been-called)))
 
 (describe "cider-classpath-entries"

Reply via email to