Subject: Re: bug#78995: [PATCH] ;;;autoload-expand for special macros

From: Eli Zaretskii <[email-masked]>
Date: Sat, 02 Aug 2025 09:52:32 +0300

> From: "J.D. Smith" <[email-masked]>
> Date: Fri, 01 Aug 2025 18:37:28 -0400
> 
> JD Smith <[email-masked]> writes:
> 
> Stefan, Eli, any comments on the documentation (or other) parts of this
> patch?

Some, see below.

> --- a/doc/lispref/functions.texi
> +++ b/doc/lispref/functions.texi
> @@ -2633,6 +2633,11 @@ Declare Form
>  This is typically used for macros, though it works for functions too.
>  @xref{Indenting Macros}.
>  
> +@item (autoload-macro @var{value})
> +This is used when defining a macro.  If @var{value} is @samp{expand},
> +any calls to the macro which follow an autoload comment will first be
> +expanded during generation of the autoloads.  @xref{Autoload}.

This can benefit from an index entry.

> --- a/doc/lispref/loading.texi
> +++ b/doc/lispref/loading.texi
> @@ -710,8 +710,31 @@ Autoload
>    If you write a function definition with an unusual macro that is not
>  one of the known and recognized function definition methods, use of an
>  ordinary magic autoload comment would copy the whole definition into
> -@code{loaddefs.el}.  That is not desirable.  You can put the desired
> -@code{autoload} call into @code{loaddefs.el} instead by writing this:
> +@code{loaddefs.el}.  That is often not desirable.  In this case, you can
                                                      ^^^^^^^^^^^^
It is not clear what case is alluded here as "this case".  Suggest
instead to say something like "If you don't want the macro copied to
@file{loaddefs.el}, you can ..."  Or to make a simple sentence like
"If this is not desirable, you can ...".

Btw, please use @file{loaddefs.el}, as @code is wrong when referring
to files.

> +use the special declare form @code{(autoload-macro expand)} in your
> +macro definition (@pxref{Declare Form}).  This indicates to the autoload
> +system that calls to your macro following @samp{;;;###autoload} should
> +be expanded, similar to how the special functions listed above are
> +handled.  For example, a macro which wraps @code{define-minor-mode} can
> +request expansion, so that proper @code{autoload} calls for the function
> +it defines are generated.

This text left me wondering how this is different from not using
autoload-macro.  When you describe some measure to avoid an
undesirable outcome, please always be sure to say what will happen
under if that measure is taken, as opposed to when it isn't.  Here,
you say what autoload-macro does, but saying that the macro is
expanded doesn't help, because it is expanded even if autoload-macro
is not used (right?).

> +The symbol @code{:autoload-end} can be used in the expansion to prevent
> +including unwanted forms in the autoload output.  Its presence causes
> +any further elements within the list where it appears to be silently
> +skipped.

An index entry is missing here.  Also, the description is complex
enough to call for an example as a means to clarify it.

> +Note that, if a symbol in the car of an autoloaded form is not defined
                                 ^^^
"car" should be in @code.

> +during autoload generation, the file in which the associated
> +@samp{;;;###autoload} appears will itself be loaded, to give the macro a
> +chance to be defined.  Packages which use special loading mechanisms,
> +including requiring their own @samp{foo-autoload.el} file, should
> +therefore gracefully handle load errors, since these files may not yet
> +exist during autoload generation.  This can be done, e.g., by setting
> +the @var{no-error} argument of @code{require} non-@code{nil}).

Please add here a cross-reference to where 'require' is defined.

> --- a/lisp/emacs-lisp/byte-run.el
> +++ b/lisp/emacs-lisp/byte-run.el
> @@ -271,6 +271,11 @@ 'byte-run--set-debug
>  	    (list 'put (list 'quote name)
>  		  ''edebug-form-spec (list 'quote spec)))))
>  
> +(defalias 'byte-run--set-autoload-macro
> +  #'(lambda (name _args spec)
> +      (list 'function-put (list 'quote name)
> +	    ''autoload-macro (list 'quote spec))))

Can we have a comment here explaining the reason for the use of
autoload-macro?

>  (defalias 'byte-run--set-no-font-lock-keyword
>    #'(lambda (name _args val)
>        (list 'function-put (list 'quote name)
> @@ -350,8 +355,10 @@ macro-declarations-alist
>    (cons
>     (list 'debug #'byte-run--set-debug)
>     (cons
> -    (list 'no-font-lock-keyword #'byte-run--set-no-font-lock-keyword)
> -    defun-declarations-alist))
> +    (list 'autoload-macro #'byte-run--set-autoload-macro)

And maybe here as well?

> --- a/lisp/emacs-lisp/easy-mmode.el
> +++ b/lisp/emacs-lisp/easy-mmode.el
> @@ -222,10 +222,11 @@ define-minor-mode
>             (indent defun)
>             (debug (&define name string-or-null-p
>  			   [&optional [&not keywordp] sexp
> -			    &optional [&not keywordp] sexp
> -			    &optional [&not keywordp] sexp]
> +			              &optional [&not keywordp] sexp
> +			              &optional [&not keywordp] sexp]
>  			   [&rest [keywordp sexp]]
> -			   def-body)))
> +			   def-body))
> +           (autoload-macro expand))

And here.

> @@ -487,7 +488,7 @@ define-globalized-minor-mode
>  on if the hook has explicitly disabled it.
>  
>  \(fn GLOBAL-MODE MODE TURN-ON [KEY VALUE]... BODY...)"
> -  (declare (doc-string 2) (indent defun))
> +  (declare (doc-string 2) (indent defun) (autoload-macro expand))

And here.

> --- a/lisp/emacs-lisp/inline.el
> +++ b/lisp/emacs-lisp/inline.el
> @@ -135,7 +135,7 @@ define-inline
>  This is like `defmacro', but has several advantages.
>  See Info node `(elisp)Defining Functions' for more details."
>    ;; FIXME: How can this work with CL arglists?
> -  (declare (indent defun) (debug defun) (doc-string 3))
> +  (declare (indent defun) (debug defun) (doc-string 3) (autoload-macro expand))

And here.

> --- a/lisp/emacs-lisp/pcase.el
> +++ b/lisp/emacs-lisp/pcase.el
> @@ -537,7 +537,7 @@ pcase-defmacro
>  By convention, DOC should use \"EXPVAL\" to stand
>  for the result of evaluating EXP (first arg to `pcase').
>  \n(fn NAME ARGS [DOC] &rest BODY...)"
> -  (declare (indent 2) (debug defun) (doc-string 3))
> +  (declare (indent 2) (debug defun) (doc-string 3) (autoload-macro expand))

And here.

> --- a/lisp/emacs-lisp/loaddefs-gen.el
> +++ b/lisp/emacs-lisp/loaddefs-gen.el
> @@ -142,12 +142,29 @@ loaddefs-generate--shorten-autoload
>               3)
>          form))
>  
> +;; TODO: some macros can be removed from this list once they
> +;;  (declare ((autoload-macro expand) ...)
> +(defconst loaddefs--defining-macros
> +  '(define-skeleton define-derived-mode
> +     define-compilation-mode define-generic-mode
> +     easy-mmode-define-global-mode define-global-minor-mode
> +     define-globalized-minor-mode
> +     easy-mmode-define-minor-mode define-minor-mode
> +     cl-defun defun* cl-defmacro defmacro*
> +     define-overloadable-function
> +     transient-define-prefix transient-define-suffix
> +     transient-define-infix transient-define-argument))

Please explain in a comment which macros should appear the above list,
and why.

Thanks.