Subject: Re: bug#73881: 31.0.50; Unexpected warnings about recursive occurrences of obsolete functions

From: Stefan Monnier <[email-masked]>
Date: Sun, 27 Oct 2024 15:18:51 -0400

>> ..Hmm.. I think I see the problem: the code I wrote was for variables
>> rather than for functions:
>>
>>     ;; If foo.el declares `toto' as obsolete, it is likely that foo.el will
>>     ;; actually use `toto' in order for this obsolete variable to still work
>>     ;; correctly, so paradoxically, while byte-compiling foo.el, the presence
>>     ;; of a make-obsolete-variable call for `toto' is an indication that `toto'
>>     ;; should not trigger obsolete-warnings in foo.el.
>>     (byte-defop-compiler-1 make-obsolete-variable)
>>     (defun byte-compile-make-obsolete-variable (form)
>>       (when (eq 'quote (car-safe (nth 1 form)))
>>         (push (nth 1 (nth 1 form)) byte-compile-global-not-obsolete-vars))
>>       (byte-compile-normal-call form))
>>
>> So maybe we should just do the same for `make-obsolete`?
>
> I think that makes sense.

I propose the patch below (which also fixes a leak of
`byte-compile-global-not-obsolete-vars` to code compiled from other
files).
But I don't think it fixes the OP's problem because

    (macroexpand '(defun my-foo () (declare (obsolete "blabla" "25")) 42))
=>
    (prog1 (defalias 'my-foo #'(lambda nil ...))
      (make-obsolete 'my-foo '"blabla" "25"))

IOW, the `make-obsolete` comes *after* the function and thus my new code
will only silence warnings for calls to `my-foo` that are present
*after* the function definition.

        Stefan

diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index f058fc48cc7..efa1ea6b676 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -433,8 +433,6 @@ byte-compile-interactive-only-functions
 
 (defvar byte-compile-not-obsolete-vars nil
   "List of variables that shouldn't be reported as obsolete.")
-(defvar byte-compile-global-not-obsolete-vars nil
-  "Global list of variables that shouldn't be reported as obsolete.")
 
 (defvar byte-compile-not-obsolete-funcs nil
   "List of functions that shouldn't be reported as obsolete.")
@@ -1909,6 +1907,8 @@ byte-compile-close-variables
          (byte-compile-const-variables nil)
          (byte-compile-free-references nil)
          (byte-compile-free-assignments nil)
+         (byte-compile-not-obsolete-vars nil)
+         (byte-compile-not-obsolete-funcs nil)
          ;;
          ;; Close over these variables so that `byte-compiler-options'
          ;; can change them on a per-file basis.
@@ -2764,6 +2764,8 @@ byte-compile-file-form-with-suppressed-warnings
 ;; Automatically evaluate define-obsolete-function-alias etc at top-level.
 (put 'make-obsolete 'byte-hunk-handler 'byte-compile-file-form-make-obsolete)
 (defun byte-compile-file-form-make-obsolete (form)
+  (when (eq 'quote (car-safe (nth 1 form)))
+    (push (nth 1 (nth 1 form)) byte-compile-not-obsolete-funcs))
   (prog1 (byte-compile-keep-pending form)
     (apply 'make-obsolete
            (mapcar 'eval (cdr form)))))
@@ -3808,7 +3810,6 @@ byte-compile-check-variable
 	((let ((od (get var 'byte-obsolete-variable)))
            (and od
                 (not (memq var byte-compile-not-obsolete-vars))
-                (not (memq var byte-compile-global-not-obsolete-vars))
                 (not (memq var byte-compile-lexical-variables))
                 (pcase (nth 1 od)
                   ('set (not (eq access-type 'reference)))
@@ -5068,7 +5069,7 @@ lambda
 (byte-defop-compiler-1 make-obsolete-variable)
 (defun byte-compile-make-obsolete-variable (form)
   (when (eq 'quote (car-safe (nth 1 form)))
-    (push (nth 1 (nth 1 form)) byte-compile-global-not-obsolete-vars))
+    (push (nth 1 (nth 1 form)) byte-compile-not-obsolete-vars))
   (byte-compile-normal-call form))
 
 (defun byte-compile-defvar (form &optional toplevel)