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"