Subject: Re: bug#65620: void function edebug-after

From: Alan Mackenzie <[email-masked]>
Date: Fri, 1 Sep 2023 21:27:12 +0000

Hello, Gerd.

On Fri, Sep 01, 2023 at 14:27:42 +0200, Gerd Möllmann wrote:
> On 01.09.23 11:23, Alan Mackenzie wrote:

> > I think a better way of handling this would be to have a "base function"
> > for edebug-after (and for edebug-before), as opposed to the nil that each
> > of these currently has.  These functions would throw an error asking the
> > user to check the edebug spec.  Something like (untested):
> > 
> >      (defun edebug-after (before-index after-index form)
> >        "Version of `edebug-after' to call when edebug is not yet set up.
> >      This function gets temporarily replaced by a real function when
> >      edebug becomes active."
> >        (error "Invalid call to `edebug-after' for %S: Is your debug spec \
> >      correct?" form))
> > 
> > ..  What do you think?

> I find that an excellent idea!  The error "void function edebug-after" 
> might indeed be considered a bit unhelpful by some :-).  Haven't tested 
> anything either, though...

Here's a working patch with a slight improvement: the error message
identifies the macro suspected of having an erroneous edebug spec.

diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
index 9a06807bcdc..a6153d599ac 100644
--- a/lisp/emacs-lisp/edebug.el
+++ b/lisp/emacs-lisp/edebug.el
@@ -2469,12 +2469,50 @@ edebug-run-fast
   (setf (cdr (assq 'edebug edebug-behavior-alist))
         '(edebug-default-enter edebug-fast-before edebug-fast-after)))
 
-(defalias 'edebug-before nil
+;; The following versions of `edebug-before' and `edebug-after' exist
+;; to handle the error which occurs if either of them gets called
+;; without an enclosing `edebug-enter'.  This can happen, for example,
+;; when a macro mistakenly has a `form' element in its edebug spec,
+;; and it additionally, at macro-expansion time, calls `eval',
+;; `apply', or `funcall' (etc.) on the corresponding argument.  This
+;; is intended to fix bug#65620.
+
+(defun edebug-b/a-error (func)
+  "Throw an error for an invalid call of FUNC.
+FUNC is expected to be `edebug-before' or `edebug-after'."
+  (let (this-macro
+        (n 0)
+        bt-frame)
+    (while (and (setq bt-frame (backtrace-frame n))
+                (not (and (car bt-frame)
+                          (eq (cadr bt-frame) 'macroexpand-1))))
+      (setq n (1+ n)))
+    (when bt-frame
+      (setq this-macro (caaddr bt-frame)))
+
+    (error
+     (concat "Invalid call to `" (symbol-name func) "'"
+             (if this-macro
+                 (concat ".  Is the edebug spec for `"
+                         (symbol-name this-macro)
+                         "' correct?")
+               "")))))
+
+(defun edebug-before (_before-index)
   "Function called by Edebug before a form is evaluated.
-See `edebug-behavior-alist' for implementations.")
-(defalias 'edebug-after nil
+See `edebug-behavior-alist' for other implementations.  This
+version of `edebug-before' gets called when edebug is not yet set
+up.  `edebug-enter' binds the function cell to a real function
+when edebug becomes active."
+  (edebug-b/a-error 'edebug-before))
+
+(defun edebug-after (_before-index _after-index _form)
   "Function called by Edebug after a form is evaluated.
-See `edebug-behavior-alist' for implementations.")
+See `edebug-behavior-alist' for other implementations.  This
+version of `edebug-after' gets called when edebug is not yet set
+up.  `edebug-enter' binds the function cell to a real function
+when edebug becomes active."
+  (edebug-b/a-error 'edebug-after))
 
 (defun edebug--update-coverage (after-index value)
   (let ((old-result (aref edebug-coverage after-index)))

-- 
Alan Mackenzie (Nuremberg, Germany).