diff options
author | Stefan Monnier <monnier@iro.umontreal.ca> | 2023-02-04 11:23:31 -0500 |
---|---|---|
committer | Stefan Monnier <monnier@iro.umontreal.ca> | 2023-02-04 11:23:31 -0500 |
commit | c39c26e33f6bb45479bbd1a80df8c97cf750a56a (patch) | |
tree | 0632a99a1c20488637fc7fe108c30bab7e3d5055 /lisp/emacs-lisp/nadvice.el | |
parent | 229d0772e235f51812ed8020a31f9a8de366c7ba (diff) | |
download | emacs-c39c26e33f6bb45479bbd1a80df8c97cf750a56a.tar.gz emacs-c39c26e33f6bb45479bbd1a80df8c97cf750a56a.tar.bz2 emacs-c39c26e33f6bb45479bbd1a80df8c97cf750a56a.zip |
nadvice: Fix bug#61179
Advising interactive forms relies on the ability to distinguish
interactive forms that do nothing else than return a function.
So, be careful to preserve this info.
Furthermore, interactive forms are expected to be evaluated in
the lexical context captured by the closure to which they belong,
so be careful to preserve that context when manipulating those forms.
* lisp/emacs-lisp/cconv.el (cconv-convert, cconv-analyze-form) <lambda>:
Preserve the info that an interactive form does nothing else than
return a function.
* lisp/emacs-lisp/nadvice.el (advice--interactive-form-1): New function.
(advice--interactive-form): Use it.
(advice--make-interactive-form): Refine to also accept function values
quoted with `quote`. Remove obsolete TODO.
* test/lisp/emacs-lisp/nadvice-tests.el: Don't disallow byte-compilation.
(advice-test-bug61179): New test.
* lisp/emacs-lisp/oclosure.el (cconv--interactive-helper): Allow
the `if` arg to be a form.
* lisp/simple.el (oclosure-interactive-form): Adjust accordingly.
Diffstat (limited to 'lisp/emacs-lisp/nadvice.el')
-rw-r--r-- | lisp/emacs-lisp/nadvice.el | 30 |
1 files changed, 24 insertions, 6 deletions
diff --git a/lisp/emacs-lisp/nadvice.el b/lisp/emacs-lisp/nadvice.el index 85934d9ed0a..e457387acc9 100644 --- a/lisp/emacs-lisp/nadvice.el +++ b/lisp/emacs-lisp/nadvice.el @@ -178,20 +178,38 @@ DOC is a string where \"FUNCTION\" and \"OLDFUN\" are expected.") ;; ((functionp spec) (funcall spec)) (t (eval spec)))) +(defun advice--interactive-form-1 (function) + "Like `interactive-form' but preserves the static context if needed." + (let ((if (interactive-form function))) + (if (or (null if) (not (eq 'closure (car-safe function)))) + if + (cl-assert (eq 'interactive (car if))) + (let ((form (cadr if))) + (if (macroexp-const-p form) + if + ;; The interactive is expected to be run in the static context + ;; that the function captured. + (let ((ctx (nth 1 function))) + `(interactive + ,(let* ((f (if (eq 'function (car-safe form)) (cadr form) form))) + ;; If the form jut returns a function, preserve the fact that + ;; it just returns a function, which is an info we use in + ;; `advice--make-interactive-form'. + (if (eq 'lambda (car-safe f)) + `',(eval form ctx) + `(eval ',form ',ctx)))))))))) + (defun advice--interactive-form (function) "Like `interactive-form' but tries to avoid autoloading functions." (if (not (and (symbolp function) (autoloadp (indirect-function function)))) - (interactive-form function) + (advice--interactive-form-1 function) (when (commandp function) `(interactive (advice-eval-interactive-spec - (cadr (interactive-form ',function))))))) + (cadr (advice--interactive-form-1 ',function))))))) (defun advice--make-interactive-form (iff ifm) - ;; TODO: make it so that interactive spec can be a constant which - ;; dynamically checks the advice--car/cdr to do its job. - ;; For that, advice-eval-interactive-spec needs to be more faithful. (let* ((fspec (cadr iff))) - (when (eq 'function (car-safe fspec)) ;; Macroexpanded lambda? + (when (memq (car-safe fspec) '(function quote)) ;; Macroexpanded lambda? (setq fspec (eval fspec t))) (if (functionp fspec) `(funcall ',fspec ',(cadr ifm)) |