Repository: hylang/hy

Issue:
&kwonly in defmacro
```
=> (defmacro foo [&kwonly b] `(print ~b))
import hy
from hy import HyExpression, HySymbol

@hy.macros.macro('foo')
def _hy_anon_fn_1(*, b):
    return HyExpression((([] + [HySymbol('print')]) + [b]))
=> (foo)
  File "<input>", line 1, column 1

  (foo)
  ^---^
HyMacroExpansionError: expanding `foo': TypeError("_hy_anon_fn_1() missing 1 req
uired keyword-only argument: 'b'",)

=> (foo :b 42)
  File "<input>", line 1, column 1

  (foo :b 42)
  ^---------^
HyMacroExpansionError: expanding `foo': TypeError('_hy_anon_fn_1() takes 0 posit
ional arguments but 2 were given',)

=> (foo ':b 42)
  File "<input>", line 1, column 1

  (foo ':b 42)
  ^----------^
HyMacroExpansionError: expanding `foo': TypeError('_hy_anon_fn_1() takes 0 posit
ional arguments but 2 were given',)

=> (macroexpand '(foo :b 42))
from hy.core.language import macroexpand
from hy import HyExpression, HyInteger, HyKeyword, HySymbol
macroexpand(HyExpression(((([] + [HySymbol('foo')]) + [HyKeyword('\ufdd0:b')]) +
 [HyInteger(42)])))
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "C:\Users\ME\Code\repos\hy/hy/core/language.hy", line 265, in macroexpand

    (hy.macros.macroexpand form name))
  File "C:\Python34\Scripts\env-hy\lib\site-packages\hy\macros.py", line 155, in
 macroexpand
    tree = macroexpand_1(tree, module_name)
  File "C:\Python34\Scripts\env-hy\lib\site-packages\hy\macros.py", line 194, in
 macroexpand_1
    raise HyMacroExpansionError(tree, msg)
hy.errors.HyMacroExpansionError: <unprintable HyMacroExpansionError object>
```

The keywords appear to work as simple positional arguments. If there's no way to provide a `&kwonly` argument to a macro form, then it should not be allowed in `defmacro`.

Either change the macro system to accept keyword arguments, or signal an error when someone attempts to use `&kwonly` etc. in `defmacro`.



Discussion:

Resolving patch:
diff --git a/hy/compiler.py b/hy/compiler.py
--- a/hy/compiler.py
+++ b/hy/compiler.py
@@ -2428,6 +2428,9 @@ def compile_macro(self, expression):
             raise HyTypeError(name, ("received a `%s' instead of a symbol "
                                      "for macro name" % type(name).__name__))
         name = HyString(name).replace(name)
+        for kw in ("&kwonly", "&kwargs", "&key"):
+            if kw in expression[0]:
+                raise HyTypeError(name, "macros cannot use %s" % kw)
         new_expression = HyExpression([
             HySymbol("with_decorator"),
             HyExpression([HySymbol("hy.macros.macro"), name]),


Test patch:
diff --git a/tests/native_tests/native_macros.hy b/tests/native_tests/native_macros.hy
--- a/tests/native_tests/native_macros.hy
+++ b/tests/native_tests/native_macros.hy
@@ -1,3 +1,5 @@
+(import [hy.errors [HyTypeError]])
+
 (defmacro rev [&rest body]
   "Execute the `body` statements in reverse"
   (quasiquote (do (unquote-splice (list (reversed body))))))
@@ -48,6 +50,26 @@
 (defmacro bar [x y]
   (foo x y))
 
+(defn test-macro-kw []
+  "NATIVE: test that an error is raised when &kwonly, &kwargs, or &key is used in a macro"
+  (try
+    (eval '(defmacro f [&kwonly a b]))
+    (except [e HyTypeError]
+      (assert (= e.message "macros cannot use &kwonly")))
+    (else (assert false)))
+
+  (try
+    (eval '(defmacro f [&kwargs kw]))
+    (except [e HyTypeError]
+      (assert (= e.message "macros cannot use &kwargs")))
+    (else (assert false)))
+
+  (try
+    (eval '(defmacro f [&key {"kw" "xyz"}]))
+    (except [e HyTypeError]
+      (assert (= e.message "macros cannot use &key")))
+    (else (assert false))))
+
 (defn test-fn-calling-macro []
   "NATIVE: test macro calling a plain function"
   (assert (= 3 (bar 1 2))))