aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2022-01-31 11:07:06 -0500
committerStefan Monnier2022-01-31 11:07:26 -0500
commit1d1b664fbb9232aa40d8daa54a689cfd63d38aa9 (patch)
tree164ad10242c8566c3c8fa1d0c12c50804f82d791
parent90bbf27f02b1e7bf9cc0f0206313795c210c565b (diff)
downloademacs-1d1b664fbb9232aa40d8daa54a689cfd63d38aa9.tar.gz
emacs-1d1b664fbb9232aa40d8daa54a689cfd63d38aa9.zip
(function-history): New symbol property (bug#53632)
Rework the code we have in Fdefalias that tries to keep track of definitions so as to be able to undo them later. We used to store in `load-history` when an autoload is redefined as a non-autoload and in the `autoload` symbol property we used to store the autoload data that used to be used before it got overriden. Instead, store the history of the function definition of a symbol in its `function-history` symbol property. To make this list cheap in the default case, the latest value is not stored in the list (since it's in the `symbol-function`) and neither is the first file. So if there's only been a single definition (the most common case), the list is empty and the property is just not present at all. The patch also gets rid of the `autoload` vs `defun` distinction in `load-history` which seems unnecessary (a significant part of the motivation for this patch was to get rid of the special handling of autoloads in this part of the code). * src/data.c (add_to_function_history): New function. (defalias): Use it. Don't add the `t` entries for autoloads and always use `defun` regardless of the kind of definition. Change `Vautoload_queue` to only hold the function symbols since the rest is now available from `function-history`. * src/eval.c (un_autoload): Adjust accordingly. * src/lread.c (load-history): Udate docstring. * lisp/loadhist.el (loadhist-unload-filename): New var. (unload-feature): Bind it. (loadhist-unload-element): Document its availability. (loadhist--restore-autoload): Delete var. (loadhist--unload-function): Delete function. (loadhist-unload-element): Delete the `t` and `autoload` methods. Rewrite the `defun` method using `function-history`. * lisp/help-fns.el: Require `seq`. (help-fns--autoloaded-p): Rewrite. (help-fns-function-description-header): Adjust call accordingly. * doc/lispref/loading.texi (Where Defined): Remove `autoload` and `t` entries from `load-history` since we don't generate them any more. Document the `function-history` which replaces the `autoload` property. (Unloading): Adjust symbol property name accordingly. * test/lisp/loadhist-resources/loadhist--bar.el: * test/lisp/loadhist-resources/loadhist--foo.el: New files. * test/lisp/loadhist-tests.el (loadhist-tests-unload-feature-nested) (loadhist-tests-unload-feature-notnested): New tests.
-rw-r--r--doc/lispref/loading.texi24
-rw-r--r--etc/NEWS6
-rw-r--r--lisp/help-fns.el23
-rw-r--r--lisp/loadhist.el54
-rw-r--r--src/data.c62
-rw-r--r--src/eval.c14
-rw-r--r--src/lread.c9
-rw-r--r--test/lisp/loadhist-resources/loadhist--bar.el27
-rw-r--r--test/lisp/loadhist-resources/loadhist--foo.el29
-rw-r--r--test/lisp/loadhist-tests.el47
10 files changed, 218 insertions, 77 deletions
diff --git a/doc/lispref/loading.texi b/doc/lispref/loading.texi
index 5957b8ac385..6179270dd3d 100644
--- a/doc/lispref/loading.texi
+++ b/doc/lispref/loading.texi
@@ -1067,13 +1067,8 @@ list elements have these forms:
1067The symbol @var{var} was defined as a variable. 1067The symbol @var{var} was defined as a variable.
1068@item (defun . @var{fun}) 1068@item (defun . @var{fun})
1069The function @var{fun} was defined. 1069The function @var{fun} was defined.
1070@item (t . @var{fun})
1071The function @var{fun} was previously an autoload before this library
1072redefined it as a function. The following element is always
1073@code{(defun . @var{fun})}, which represents defining @var{fun} as a 1070@code{(defun . @var{fun})}, which represents defining @var{fun} as a
1074function. 1071function.
1075@item (autoload . @var{fun})
1076The function @var{fun} was defined as an autoload.
1077@item (defface . @var{face}) 1072@item (defface . @var{face})
1078The face @var{face} was defined. 1073The face @var{face} was defined.
1079@item (require . @var{feature}) 1074@item (require . @var{feature})
@@ -1096,6 +1091,23 @@ The value of @code{load-history} may have one element whose @sc{car} is
1096by adding the symbols defined to the element for the file being visited, 1091by adding the symbols defined to the element for the file being visited,
1097rather than replacing that element. @xref{Eval}. 1092rather than replacing that element. @xref{Eval}.
1098 1093
1094In addition to @code{load-history}, every function keeps track of its
1095own history in the symbol property @code{function-history}.
1096The reason why functions are treated specially in this respect is that
1097it is common for functions to be defined in two steps in two different
1098files (typically, one of them is an autoload), so in order to be
1099able to properly @emph{unload} a file, we need to know more precisely
1100what that file did to the function definition.
1101
1102@kindex{function-history}
1103The symbol property @code{function-history} holds a list of the form
1104@code{(@var{file1} @var{def2} @var{file2} @var{def3} ...)} where
1105@var{file1} is the last file that changed the definition and
1106@var{def2} was the definition before @var{file1}, set by @var{file2},
1107etc... Logically this list should end with the name of the first file
1108that defined this function, but to save space this last element
1109is usually omitted.
1110
1099@node Unloading 1111@node Unloading
1100@section Unloading 1112@section Unloading
1101@cindex unloading packages 1113@cindex unloading packages
@@ -1110,7 +1122,7 @@ It undefines all functions, macros, and variables defined in that
1110library with @code{defun}, @code{defalias}, @code{defsubst}, 1122library with @code{defun}, @code{defalias}, @code{defsubst},
1111@code{defmacro}, @code{defconst}, @code{defvar}, and @code{defcustom}. 1123@code{defmacro}, @code{defconst}, @code{defvar}, and @code{defcustom}.
1112It then restores any autoloads formerly associated with those symbols. 1124It then restores any autoloads formerly associated with those symbols.
1113(Loading saves these in the @code{autoload} property of the symbol.) 1125(Loading saves these in the @code{function-history} property of the symbol.)
1114 1126
1115Before restoring the previous definitions, @code{unload-feature} runs 1127Before restoring the previous definitions, @code{unload-feature} runs
1116@code{remove-hook} to remove functions defined by the library from certain 1128@code{remove-hook} to remove functions defined by the library from certain
diff --git a/etc/NEWS b/etc/NEWS
index 4583cfb0952..5d4a59975c2 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -129,6 +129,9 @@ example, if point is before an Emoji sequence, pressing <Delete> will
129delete the entire sequence, not just a single character at its 129delete the entire sequence, not just a single character at its
130beginning. 130beginning.
131 131
132** 'load-history' does not treat autoloads specially any more.
133An autoload definition appears just as a (defun . NAME) and the
134(t . NAME) entries are not generated any more.
132 135
133* Changes in Emacs 29.1 136* Changes in Emacs 29.1
134 137
@@ -1471,6 +1474,9 @@ The property ':position' now specifies the position of the underline
1471when used as part of a property list specification for the 1474when used as part of a property list specification for the
1472':underline' attribute. 1475':underline' attribute.
1473 1476
1477** 'defalias' records a more precise history of definitions.
1478This is recorded in the `function-history` symbol property.
1479
1474 1480
1475* Changes in Emacs 29.1 on Non-Free Operating Systems 1481* Changes in Emacs 29.1 on Non-Free Operating Systems
1476 1482
diff --git a/lisp/help-fns.el b/lisp/help-fns.el
index 98a1b11e088..36c7966919b 100644
--- a/lisp/help-fns.el
+++ b/lisp/help-fns.el
@@ -33,6 +33,7 @@
33;;; Code: 33;;; Code:
34 34
35(require 'cl-lib) 35(require 'cl-lib)
36(require 'seq)
36(require 'help-mode) 37(require 'help-mode)
37(require 'radix-tree) 38(require 'radix-tree)
38(eval-when-compile (require 'subr-x)) ;For when-let. 39(eval-when-compile (require 'subr-x)) ;For when-let.
@@ -678,19 +679,9 @@ suitable file is found, return nil."
678 (terpri))) 679 (terpri)))
679 680
680;; We could use `symbol-file' but this is a wee bit more efficient. 681;; We could use `symbol-file' but this is a wee bit more efficient.
681(defun help-fns--autoloaded-p (function file) 682(defun help-fns--autoloaded-p (function)
682 "Return non-nil if FUNCTION has previously been autoloaded. 683 "Return non-nil if FUNCTION has previously been autoloaded."
683FILE is the file where FUNCTION was probably defined." 684 (seq-some #'autoloadp (get function 'function-history)))
684 (let* ((file (file-name-sans-extension (file-truename file)))
685 (load-hist load-history)
686 (target (cons t function))
687 found)
688 (while (and load-hist (not found))
689 (and (stringp (caar load-hist))
690 (equal (file-name-sans-extension (caar load-hist)) file)
691 (setq found (member target (cdar load-hist))))
692 (setq load-hist (cdr load-hist)))
693 found))
694 685
695(defun help-fns--interactive-only (function) 686(defun help-fns--interactive-only (function)
696 "Insert some help blurb if FUNCTION should only be used interactively." 687 "Insert some help blurb if FUNCTION should only be used interactively."
@@ -873,13 +864,13 @@ Returns a list of the form (REAL-FUNCTION DEF ALIASED REAL-DEF)."
873 "Print a line describing FUNCTION to `standard-output'." 864 "Print a line describing FUNCTION to `standard-output'."
874 (pcase-let* ((`(,_real-function ,def ,aliased ,real-def) 865 (pcase-let* ((`(,_real-function ,def ,aliased ,real-def)
875 (help-fns--analyze-function function)) 866 (help-fns--analyze-function function))
876 (file-name (find-lisp-object-file-name function (if aliased 'defun 867 (file-name (find-lisp-object-file-name
877 def))) 868 function (if aliased 'defun def)))
878 (beg (if (and (or (byte-code-function-p def) 869 (beg (if (and (or (byte-code-function-p def)
879 (keymapp def) 870 (keymapp def)
880 (memq (car-safe def) '(macro lambda closure))) 871 (memq (car-safe def) '(macro lambda closure)))
881 (stringp file-name) 872 (stringp file-name)
882 (help-fns--autoloaded-p function file-name)) 873 (help-fns--autoloaded-p function))
883 (concat 874 (concat
884 "an autoloaded " (if (commandp def) 875 "an autoloaded " (if (commandp def)
885 "interactive ")) 876 "interactive "))
diff --git a/lisp/loadhist.el b/lisp/loadhist.el
index 48058f40535..39481ab0684 100644
--- a/lisp/loadhist.el
+++ b/lisp/loadhist.el
@@ -157,38 +157,35 @@ documentation of `unload-feature' for details.")
157 ;; mode, or proposed is not nil and not major-mode, and so we use it. 157 ;; mode, or proposed is not nil and not major-mode, and so we use it.
158 (funcall (or proposed 'fundamental-mode))))))) 158 (funcall (or proposed 'fundamental-mode)))))))
159 159
160(defvar loadhist-unload-filename nil)
161
160(cl-defgeneric loadhist-unload-element (x) 162(cl-defgeneric loadhist-unload-element (x)
161 "Unload an element from the `load-history'." 163 "Unload an element from the `load-history'.
164The variable `loadhist-unload-filename' holds the name of the file we're
165unloading."
162 (message "Unexpected element %S in load-history" x)) 166 (message "Unexpected element %S in load-history" x))
163 167
164;; In `load-history', the definition of a previously autoloaded
165;; function is represented by 2 entries: (t . SYMBOL) comes before
166;; (defun . SYMBOL) and says we should restore SYMBOL's autoload when
167;; we undefine it.
168;; So we use this auxiliary variable to keep track of the last (t . SYMBOL)
169;; that occurred.
170(defvar loadhist--restore-autoload nil
171 "If non-nil, is a symbol for which to try to restore a previous autoload.")
172
173(cl-defmethod loadhist-unload-element ((x (head t)))
174 (setq loadhist--restore-autoload (cdr x)))
175
176(defun loadhist--unload-function (x)
177 (let ((fun (cdr x)))
178 (when (fboundp fun)
179 (when (fboundp 'ad-unadvise)
180 (ad-unadvise fun))
181 (let ((aload (get fun 'autoload)))
182 (defalias fun
183 (if (and aload (eq fun loadhist--restore-autoload))
184 (cons 'autoload aload)
185 nil)))))
186 (setq loadhist--restore-autoload nil))
187
188(cl-defmethod loadhist-unload-element ((x (head defun))) 168(cl-defmethod loadhist-unload-element ((x (head defun)))
189 (loadhist--unload-function x)) 169 (let* ((fun (cdr x))
190(cl-defmethod loadhist-unload-element ((x (head autoload))) 170 (hist (get fun 'function-history)))
191 (loadhist--unload-function x)) 171 (cond
172 ((null hist)
173 (defalias fun nil)
174 ;; Override the change that `defalias' just recorded.
175 (put fun 'function-history nil))
176 ((equal (car hist) loadhist-unload-filename)
177 (defalias fun (cadr hist))
178 ;; Set the history afterwards, to override the change that
179 ;; `defalias' records otherwise.
180 (put fun 'function-history (cddr hist)))
181 (t
182 ;; Unloading a file whose definition is "inactive" (i.e. has been
183 ;; overridden by another file): just remove it from the history,
184 ;; so future unloading of that other file has a chance to DTRT.
185 (let* ((tmp (plist-member hist loadhist-unload-filename))
186 (pos (- (length hist) (length tmp))))
187 (cl-assert (> pos 1))
188 (setcdr (nthcdr (- pos 2) hist) (cdr tmp)))))))
192 189
193(cl-defmethod loadhist-unload-element ((_ (head require))) nil) 190(cl-defmethod loadhist-unload-element ((_ (head require))) nil)
194(cl-defmethod loadhist-unload-element ((_ (head defface))) nil) 191(cl-defmethod loadhist-unload-element ((_ (head defface))) nil)
@@ -257,6 +254,7 @@ something strange, such as redefining an Emacs function."
257 (prin1-to-string dependents) file)))) 254 (prin1-to-string dependents) file))))
258 (let* ((unload-function-defs-list (feature-symbols feature)) 255 (let* ((unload-function-defs-list (feature-symbols feature))
259 (file (pop unload-function-defs-list)) 256 (file (pop unload-function-defs-list))
257 (loadhist-unload-filename file)
260 (name (symbol-name feature)) 258 (name (symbol-name feature))
261 (unload-hook (intern-soft (concat name "-unload-hook"))) 259 (unload-hook (intern-soft (concat name "-unload-hook")))
262 (unload-func (intern-soft (concat name "-unload-function")))) 260 (unload-func (intern-soft (concat name "-unload-function"))))
diff --git a/src/data.c b/src/data.c
index a5a76a27554..95d29ac9e98 100644
--- a/src/data.c
+++ b/src/data.c
@@ -859,6 +859,43 @@ DEFUN ("fset", Ffset, Sfset, 2, 2, 0,
859 return definition; 859 return definition;
860} 860}
861 861
862static void
863add_to_function_history (Lisp_Object symbol, Lisp_Object olddef)
864{
865 eassert (!NILP (olddef));
866
867 Lisp_Object past = Fget (symbol, Qfunction_history);
868 Lisp_Object file = Qnil;
869 /* FIXME: Sadly, `Vload_file_name` gives less precise information
870 (it's sometimes non-nil when it shoujld be nil). */
871 Lisp_Object tail = Vcurrent_load_list;
872 FOR_EACH_TAIL_SAFE (tail)
873 if (NILP (XCDR (tail)) && STRINGP (XCAR (tail)))
874 file = XCAR (tail);
875
876 Lisp_Object tem = Fplist_member (past, file);
877 if (!NILP (tem))
878 { /* New def from a file used before.
879 Overwrite the previous record associated with this file. */
880 if (EQ (tem, past))
881 /* The new def is from the same file as the last change, so
882 there's nothing to do: unloading the file should revert to
883 the status before the last change rather than before this load. */
884 return;
885 Lisp_Object pastlen = Flength (past);
886 Lisp_Object temlen = Flength (tem);
887 EMACS_INT tempos = XFIXNUM (pastlen) - XFIXNUM (temlen);
888 eassert (tempos > 1);
889 Lisp_Object prev = Fnthcdr (make_fixnum (tempos - 2), past);
890 /* Remove the previous info for this file.
891 E.g. change `hist` from (... OTHERFILE DEF3 THISFILE DEF2 ...)
892 to (... OTHERFILE DEF2). */
893 XSETCDR (prev, XCDR (tem));
894 }
895 /* Push new def from new file. */
896 Fput (symbol, Qfunction_history, Fcons (file, Fcons (olddef, past)));
897}
898
862void 899void
863defalias (Lisp_Object symbol, Lisp_Object definition) 900defalias (Lisp_Object symbol, Lisp_Object definition)
864{ 901{
@@ -866,19 +903,19 @@ defalias (Lisp_Object symbol, Lisp_Object definition)
866 bool autoload = AUTOLOADP (definition); 903 bool autoload = AUTOLOADP (definition);
867 if (!will_dump_p () || !autoload) 904 if (!will_dump_p () || !autoload)
868 { /* Only add autoload entries after dumping, because the ones before are 905 { /* Only add autoload entries after dumping, because the ones before are
869 not useful and else we get loads of them from the loaddefs.el. */ 906 not useful and else we get loads of them from the loaddefs.el.
870 Lisp_Object function = XSYMBOL (symbol)->u.s.function; 907 That saves us about 110KB in the pdmp file (Jan 2022). */
871 908 LOADHIST_ATTACH (Fcons (Qdefun, symbol));
872 if (AUTOLOADP (function)) 909 }
873 /* Remember that the function was already an autoload. */ 910 }
874 LOADHIST_ATTACH (Fcons (Qt, symbol));
875 LOADHIST_ATTACH (Fcons (autoload ? Qautoload : Qdefun, symbol));
876
877 if (!NILP (Vautoload_queue) && !NILP (function))
878 Vautoload_queue = Fcons (Fcons (symbol, function), Vautoload_queue);
879 911
880 if (AUTOLOADP (function)) 912 {
881 Fput (symbol, Qautoload, XCDR (function)); 913 Lisp_Object olddef = XSYMBOL (symbol)->u.s.function;
914 if (!NILP (olddef))
915 {
916 if (!NILP (Vautoload_queue))
917 Vautoload_queue = Fcons (symbol, Vautoload_queue);
918 add_to_function_history (symbol, olddef);
882 } 919 }
883 } 920 }
884 921
@@ -4171,6 +4208,7 @@ syms_of_data (void)
4171 4208
4172 DEFSYM (Qinteractive_form, "interactive-form"); 4209 DEFSYM (Qinteractive_form, "interactive-form");
4173 DEFSYM (Qdefalias_fset_function, "defalias-fset-function"); 4210 DEFSYM (Qdefalias_fset_function, "defalias-fset-function");
4211 DEFSYM (Qfunction_history, "function-history");
4174 4212
4175 DEFSYM (Qbyte_code_function_p, "byte-code-function-p"); 4213 DEFSYM (Qbyte_code_function_p, "byte-code-function-p");
4176 4214
diff --git a/src/eval.c b/src/eval.c
index b083a00a791..1076985d097 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -2250,21 +2250,17 @@ this does nothing and returns nil. */)
2250static void 2250static void
2251un_autoload (Lisp_Object oldqueue) 2251un_autoload (Lisp_Object oldqueue)
2252{ 2252{
2253 Lisp_Object queue, first, second;
2254
2255 /* Queue to unwind is current value of Vautoload_queue. 2253 /* Queue to unwind is current value of Vautoload_queue.
2256 oldqueue is the shadowed value to leave in Vautoload_queue. */ 2254 oldqueue is the shadowed value to leave in Vautoload_queue. */
2257 queue = Vautoload_queue; 2255 Lisp_Object queue = Vautoload_queue;
2258 Vautoload_queue = oldqueue; 2256 Vautoload_queue = oldqueue;
2259 while (CONSP (queue)) 2257 while (CONSP (queue))
2260 { 2258 {
2261 first = XCAR (queue); 2259 Lisp_Object first = XCAR (queue);
2262 second = Fcdr (first); 2260 if (CONSP (first) && EQ (XCAR (first), make_fixnum (0)))
2263 first = Fcar (first); 2261 Vfeatures = XCDR (first);
2264 if (EQ (first, make_fixnum (0)))
2265 Vfeatures = second;
2266 else 2262 else
2267 Ffset (first, second); 2263 Ffset (first, Fcar (Fcdr (Fget (first, Qfunction_history))));
2268 queue = XCDR (queue); 2264 queue = XCDR (queue);
2269 } 2265 }
2270} 2266}
diff --git a/src/lread.c b/src/lread.c
index 9910db27de7..713c03243cb 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -5240,12 +5240,9 @@ for symbols and features not associated with any file.
5240The remaining ENTRIES in the alist element describe the functions and 5240The remaining ENTRIES in the alist element describe the functions and
5241variables defined in that file, the features provided, and the 5241variables defined in that file, the features provided, and the
5242features required. Each entry has the form `(provide . FEATURE)', 5242features required. Each entry has the form `(provide . FEATURE)',
5243`(require . FEATURE)', `(defun . FUNCTION)', `(autoload . SYMBOL)', 5243`(require . FEATURE)', `(defun . FUNCTION)', `(defface . SYMBOL)',
5244`(defface . SYMBOL)', `(define-type . SYMBOL)', 5244 `(define-type . SYMBOL)', or `(cl-defmethod METHOD SPECIALIZERS)'.
5245`(cl-defmethod METHOD SPECIALIZERS)', or `(t . SYMBOL)'. 5245In addition, entries may also be single symbols,
5246Entries like `(t . SYMBOL)' may precede a `(defun . FUNCTION)' entry,
5247and mean that SYMBOL was an autoload before this file redefined it
5248as a function. In addition, entries may also be single symbols,
5249which means that symbol was defined by `defvar' or `defconst'. 5246which means that symbol was defined by `defvar' or `defconst'.
5250 5247
5251During preloading, the file name recorded is relative to the main Lisp 5248During preloading, the file name recorded is relative to the main Lisp
diff --git a/test/lisp/loadhist-resources/loadhist--bar.el b/test/lisp/loadhist-resources/loadhist--bar.el
new file mode 100644
index 00000000000..5c8914ed573
--- /dev/null
+++ b/test/lisp/loadhist-resources/loadhist--bar.el
@@ -0,0 +1,27 @@
1;;; loadhist--bar.el --- Dummy package for loadhist-tests -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2022 Free Software Foundation, Inc.
4
5;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
6
7;; This program is free software; you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
9;; the Free Software Foundation, either version 3 of the License, or
10;; (at your option) any later version.
11
12;; This program is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with this program. If not, see <https://www.gnu.org/licenses/>.
19
20;;; Code:
21
22(autoload 'loadhist--foo-inc "loadhist--foo")
23
24(defun loadhist--bar-dec (x) (1- x))
25
26(provide 'loadhist--bar)
27;;; loadhist--bar.el ends here
diff --git a/test/lisp/loadhist-resources/loadhist--foo.el b/test/lisp/loadhist-resources/loadhist--foo.el
new file mode 100644
index 00000000000..3574c220135
--- /dev/null
+++ b/test/lisp/loadhist-resources/loadhist--foo.el
@@ -0,0 +1,29 @@
1;;; loadhist--foo.el --- Dummy package for loadhist-tests -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2022 Free Software Foundation, Inc.
4
5;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
6
7;; This program is free software; you can redistribute it and/or modify
8;; it under the terms of the GNU General Public License as published by
9;; the Free Software Foundation, either version 3 of the License, or
10;; (at your option) any later version.
11
12;; This program is distributed in the hope that it will be useful,
13;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15;; GNU General Public License for more details.
16
17;; You should have received a copy of the GNU General Public License
18;; along with this program. If not, see <https://www.gnu.org/licenses/>.
19
20;;; Commentary:
21
22;;; Code:
23
24(autoload 'loadhist--bar-dec "loadhist--bar")
25
26(defun loadhist--foo-inc (x) (1+ x))
27
28(provide 'loadhist--foo)
29;;; loadhist--foo.el ends here
diff --git a/test/lisp/loadhist-tests.el b/test/lisp/loadhist-tests.el
index a941ac06320..ef5fc164d34 100644
--- a/test/lisp/loadhist-tests.el
+++ b/test/lisp/loadhist-tests.el
@@ -54,4 +54,51 @@
54 (should-error (unload-feature 'dired)) 54 (should-error (unload-feature 'dired))
55 (unload-feature 'dired-x)) 55 (unload-feature 'dired-x))
56 56
57(defvar loadhist--tests-dir (file-name-directory (macroexp-file-name)))
58
59(ert-deftest loadhist-tests-unload-feature-nested ()
60 (add-to-list 'load-path (expand-file-name
61 "loadhist-resources/"
62 loadhist--tests-dir))
63 (declare-function loadhist--foo-inc "loadhist--foo")
64 (declare-function loadhist--bar-dec "loadhist--dec")
65 (load "loadhist--foo" nil t)
66 (should (and (functionp 'loadhist--bar-dec) (functionp 'loadhist--foo-inc)))
67 (should (autoloadp (symbol-function 'loadhist--bar-dec)))
68 (load "loadhist--bar" nil t)
69 (should (and (functionp 'loadhist--bar-dec) (functionp 'loadhist--foo-inc)))
70 (should (not (autoloadp (symbol-function 'loadhist--bar-dec))))
71 (should (not (autoloadp (symbol-function 'loadhist--foo-inc))))
72 (should (equal (list 40 42)
73 (list (loadhist--bar-dec 41) (loadhist--foo-inc 41))))
74 (unload-feature 'loadhist--bar)
75 (should (and (functionp 'loadhist--bar-dec) (functionp 'loadhist--foo-inc)))
76 (should (autoloadp (symbol-function 'loadhist--bar-dec)))
77 (should (not (autoloadp (symbol-function 'loadhist--foo-inc))))
78 (unload-feature 'loadhist--foo)
79 (should (null (symbol-function 'loadhist--bar-dec)))
80 (should (null (symbol-function 'loadhist--foo-inc)))
81 (should (null (get 'loadhist--bar-dec 'function-history)))
82 (should (null (get 'loadhist--foo-inc 'function-history))))
83
84(ert-deftest loadhist-tests-unload-feature-notnested ()
85 (add-to-list 'load-path (expand-file-name
86 "loadhist-resources/"
87 loadhist--tests-dir))
88 (load "loadhist--foo" nil t)
89 (load "loadhist--bar" nil t)
90 (should (equal (list 40 42)
91 (list (loadhist--bar-dec 41) (loadhist--foo-inc 41))))
92 (unload-feature 'loadhist--foo)
93 (should (functionp 'loadhist--bar-dec))
94 (should (not (autoloadp (symbol-function 'loadhist--bar-dec))))
95 (should (let ((f (symbol-function 'loadhist--foo-inc)))
96 ;; Both choices seem acceptable.
97 (or (null f) (autoloadp f))))
98 (unload-feature 'loadhist--bar)
99 (should (null (symbol-function 'loadhist--bar-dec)))
100 (should (null (symbol-function 'loadhist--foo-inc)))
101 (should (null (get 'loadhist--bar-dec 'function-history)))
102 (should (null (get 'loadhist--foo-inc 'function-history))))
103
57;;; loadhist-tests.el ends here 104;;; loadhist-tests.el ends here