Repository: hylang/hy

Issue:
document the hidden &compiler parameter in defmacro
The new `let` implementation (actually the improved `macroexpand-all` function it's built upon) required this feature. But it should be documented. `defmacro` now automatically has an `&name` parameter which is set to the name of the module it's expanding in.
```Python
=> (defmacro this-module [] `(print ~&name))
from hy import HyExpression, HySymbol
import hy
hy.macros.macro('this_module')(lambda &name: HyExpression([] + [HySymbol(
    'print')] + [&name]))

<function <lambda> at 0x00000224644B6F28>
=> (this-module)
print('__console__')

__console__
```


Discussion:
Where would be a good place to include this documentation? After looking through everything it seems like the other ampersand-prefixed argument forms are documented with `defn`, but this issue seems pretty inside baseball for being included alongside those.
Under `defmacro` or in a general reference section about macros.
#2104 has replaced `&name` with `&compiler`.

Resolving patch:
diff --git a/hy/core/result_macros.py b/hy/core/result_macros.py
--- a/hy/core/result_macros.py
+++ b/hy/core/result_macros.py
@@ -1544,10 +1544,7 @@ def E(*x): return Expression(x)
     S = Symbol
 
     compiler.warn_on_core_shadow(name)
-    fn_def = E(
-        S("fn"),
-        List([S("&compiler"), *expr[2]]),
-        *body).replace(expr)
+    fn_def = E(S("fn"), List(expr[2]), *body).replace(expr)
     if compiler.is_in_local_state():
         # We're in a local scope, so define the new macro locally.
         state = compiler.local_state_stack[-1]
diff --git a/hy/macros.py b/hy/macros.py
--- a/hy/macros.py
+++ b/hy/macros.py
@@ -43,18 +43,18 @@ def pattern_macro(names, pattern, shadow=None):
 
     def dec(fn):
         def wrapper_maker(name):
-            def wrapper(hy_compiler, *args):
+            def wrapper(_hy_compiler, *args):
 
                 if shadow and any(is_unpack("iterable", x) for x in args):
                     # Try a shadow function call with this name instead.
                     return Expression(
                         [Expression(map(Symbol, [".", "hy", "pyops", name])), *args]
-                    ).replace(hy_compiler.this)
+                    ).replace(_hy_compiler.this)
 
-                expr = hy_compiler.this
+                expr = _hy_compiler.this
 
                 if py_version_required and sys.version_info < py_version_required:
-                    raise hy_compiler._syntax_error(
+                    raise _hy_compiler._syntax_error(
                         expr,
                         "`{}` requires Python {} or later".format(
                             name, ".".join(map(str, py_version_required))
@@ -64,13 +64,13 @@ def wrapper(hy_compiler, *args):
                 try:
                     parse_tree = pattern.parse(args)
                 except NoParseError as e:
-                    raise hy_compiler._syntax_error(
+                    raise _hy_compiler._syntax_error(
                         expr[min(e.state.pos + 1, len(expr) - 1)],
                         "parse error for pattern macro '{}': {}".format(
                             name, e.msg.replace("end of input", "end of macro call")
                         ),
                     )
-                return fn(hy_compiler, expr, name, *parse_tree)
+                return fn(_hy_compiler, expr, name, *parse_tree)
 
             return wrapper
 
@@ -414,7 +414,14 @@ def macroexpand(tree, module, compiler=None, once=False, result_ok=True):
         with MacroExceptions(module, tree, compiler):
             if compiler:
                 compiler.this = tree
-            obj = m(compiler, *tree[1:])
+            obj = m(
+                # If the macro's first parameter is named
+                # `_hy_compiler`, pass in the current compiler object
+                # in its place.
+                *([compiler]
+                    if m.__code__.co_varnames[:1] == ('_hy_compiler',)
+                    else []),
+                *tree[1:])
             if isinstance(obj, (hy.compiler.Result, AST)):
                 return obj if result_ok else tree
 


Test patch:
diff --git a/tests/macros/test_macro_processor.py b/tests/macros/test_macro_processor.py
--- a/tests/macros/test_macro_processor.py
+++ b/tests/macros/test_macro_processor.py
@@ -8,7 +8,7 @@
 
 
 @macro("test")
-def tmac(ETname, *tree):
+def tmac(*tree):
     """Turn an expression into a list"""
     return List(tree)
 
diff --git a/tests/native_tests/hy_eval.hy b/tests/native_tests/hy_eval.hy
--- a/tests/native_tests/hy_eval.hy
+++ b/tests/native_tests/hy_eval.hy
@@ -211,7 +211,7 @@
 
   (assert (=
     (hy.eval '(chippy a b) :macros (dict
-      :chippy (fn [&compiler arg1 arg2]
+      :chippy (fn [arg1 arg2]
         (hy.models.Symbol (+ (str arg1) (str arg2))))))
     15))
 
@@ -233,13 +233,13 @@
     (hy.eval '(cheese))
     "gorgonzola"))
   (assert (=
-    (hy.eval '(cheese) :macros {"cheese" (fn [&compiler] "cheddar")})
+    (hy.eval '(cheese) :macros {"cheese" (fn [] "cheddar")})
     "cheddar"))
 
   ; Or even a core macro, and with no warning.
   (assert (=
     (hy.eval '(+ 1 1) :macros
-      {(hy.mangle "+") (fn [&compiler #* args]
+      {(hy.mangle "+") (fn [#* args]
         (.join "" (gfor  x args  (str (int x)))))})
     "11")))
 
diff --git a/tests/native_tests/hy_misc.hy b/tests/native_tests/hy_misc.hy
--- a/tests/native_tests/hy_misc.hy
+++ b/tests/native_tests/hy_misc.hy
@@ -33,7 +33,7 @@
     '[8 "phooey"]))
   (assert (=
     (hy.macroexpand '(chippy 1) :macros
-      {"chippy" (fn [&compiler x] `[~x ~x])})
+      {"chippy" (fn [x] `[~x ~x])})
     '[1 1]))
   ; Non-Expressions just get returned as-is.
   (defn f [])
diff --git a/tests/native_tests/macros_first_class.hy b/tests/native_tests/macros_first_class.hy
--- a/tests/native_tests/macros_first_class.hy
+++ b/tests/native_tests/macros_first_class.hy
@@ -33,9 +33,9 @@ deleting them, and retrieving their docstrings. We also test `get-macro`
 ; 2. `require` in global scope
 (require tests.resources.tlib [qplah :as global2])
 ; 3. Manually updating `_hy_macros`
-(eval-and-compile (setv (get _hy_macros "global3") (fn [&compiler]
+(eval-and-compile (setv (get _hy_macros "global3") (fn []
   "from global3")))
-(eval-and-compile (setv (get _hy_macros (hy.mangle "global☘")) (fn [&compiler]
+(eval-and-compile (setv (get _hy_macros (hy.mangle "global☘")) (fn []
   "global☘ docstring"
   "from global☘")))