diff options
| author | Mattias EngdegÄrd | 2023-02-20 15:23:12 +0100 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2023-02-21 10:42:00 +0100 |
| commit | db3fea2e5ce46229ae40aa9ca6a89964261a7a5a (patch) | |
| tree | 43264b77f2bc165a310090c40065ee6dfc750169 | |
| parent | c4c4af022d6091537fb0d748342eba5e7d3ddd23 (diff) | |
| download | emacs-db3fea2e5ce46229ae40aa9ca6a89964261a7a5a.tar.gz emacs-db3fea2e5ce46229ae40aa9ca6a89964261a7a5a.zip | |
Detect and prevent function alias loops in `fset` and `defalias`
Make `fset` and `defalias` signal an error on attempts to create
circular alias chains. This is more effective, efficient and
convenient than permitting alias loops to be created and trying to
detect them at run time each time a function is called, which is what
we have been doing until now, badly.
* lisp/help-fns.el (help-fns--analyze-function):
Don't pass obsolete argument.
* lisp/subr.el (function-alias-p):
* src/data.c (indirect_function, Findirect_function): Simplify.
Now error-free, second argument obsolete.
(Ffset): Detect loops.
* test/lisp/help-fns-tests.el (help-fns--analyze-function-recursive):
* test/lisp/subr-tests.el (test-alias-p):
Adapt tests.
* test/src/data-tests.el (data-tests-fset, data-tests-defalias): New.
* doc/lispref/eval.texi (Function Indirection):
* doc/lispref/functions.texi (Defining Functions, Function Cells):
Update manual.
* etc/NEWS: Announce.
| -rw-r--r-- | doc/lispref/eval.texi | 17 | ||||
| -rw-r--r-- | doc/lispref/functions.texi | 13 | ||||
| -rw-r--r-- | etc/NEWS | 15 | ||||
| -rw-r--r-- | lisp/help-fns.el | 2 | ||||
| -rw-r--r-- | lisp/subr.el | 30 | ||||
| -rw-r--r-- | src/data.c | 63 | ||||
| -rw-r--r-- | src/eval.c | 2 | ||||
| -rw-r--r-- | test/lisp/help-fns-tests.el | 4 | ||||
| -rw-r--r-- | test/lisp/subr-tests.el | 10 | ||||
| -rw-r--r-- | test/src/data-tests.el | 36 |
10 files changed, 103 insertions, 89 deletions
diff --git a/doc/lispref/eval.texi b/doc/lispref/eval.texi index 88e899de1e8..a45517287b7 100644 --- a/doc/lispref/eval.texi +++ b/doc/lispref/eval.texi | |||
| @@ -252,11 +252,8 @@ the original symbol. If the contents are another symbol, this | |||
| 252 | process, called @dfn{symbol function indirection}, is repeated until | 252 | process, called @dfn{symbol function indirection}, is repeated until |
| 253 | it obtains a non-symbol. @xref{Function Names}, for more information | 253 | it obtains a non-symbol. @xref{Function Names}, for more information |
| 254 | about symbol function indirection. | 254 | about symbol function indirection. |
| 255 | 255 | We eventually obtain a non-symbol, which ought to be a function or | |
| 256 | One possible consequence of this process is an infinite loop, in the | 256 | other suitable object. |
| 257 | event that a symbol's function cell refers to the same symbol. | ||
| 258 | Otherwise, we eventually obtain a non-symbol, which ought to be a | ||
| 259 | function or other suitable object. | ||
| 260 | 257 | ||
| 261 | @kindex invalid-function | 258 | @kindex invalid-function |
| 262 | More precisely, we should now have a Lisp function (a lambda | 259 | More precisely, we should now have a Lisp function (a lambda |
| @@ -332,19 +329,17 @@ or just | |||
| 332 | The built-in function @code{indirect-function} provides an easy way to | 329 | The built-in function @code{indirect-function} provides an easy way to |
| 333 | perform symbol function indirection explicitly. | 330 | perform symbol function indirection explicitly. |
| 334 | 331 | ||
| 335 | @defun indirect-function function &optional noerror | 332 | @defun indirect-function function |
| 336 | @anchor{Definition of indirect-function} | 333 | @anchor{Definition of indirect-function} |
| 337 | This function returns the meaning of @var{function} as a function. If | 334 | This function returns the meaning of @var{function} as a function. If |
| 338 | @var{function} is a symbol, then it finds @var{function}'s function | 335 | @var{function} is a symbol, then it finds @var{function}'s function |
| 339 | definition and starts over with that value. If @var{function} is not a | 336 | definition and starts over with that value. If @var{function} is not a |
| 340 | symbol, then it returns @var{function} itself. | 337 | symbol, then it returns @var{function} itself. |
| 341 | 338 | ||
| 342 | This function returns @code{nil} if the final symbol is unbound. It | 339 | This function returns @code{nil} if the final symbol is unbound. |
| 343 | signals a @code{cyclic-function-indirection} error if there is a loop | ||
| 344 | in the chain of symbols. | ||
| 345 | 340 | ||
| 346 | The optional argument @var{noerror} is obsolete, kept for backward | 341 | There is also a second, optional argument that is obsolete and has no |
| 347 | compatibility, and has no effect. | 342 | effect. |
| 348 | 343 | ||
| 349 | Here is how you could define @code{indirect-function} in Lisp: | 344 | Here is how you could define @code{indirect-function} in Lisp: |
| 350 | 345 | ||
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index f5572e447d3..b6a4ee13308 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi | |||
| @@ -737,9 +737,12 @@ explicitly in the source file being loaded. This is because | |||
| 737 | By contrast, in programs that manipulate function definitions for other | 737 | By contrast, in programs that manipulate function definitions for other |
| 738 | purposes, it is better to use @code{fset}, which does not keep such | 738 | purposes, it is better to use @code{fset}, which does not keep such |
| 739 | records. @xref{Function Cells}. | 739 | records. @xref{Function Cells}. |
| 740 | |||
| 741 | If the resulting function definition chain would be circular, then | ||
| 742 | Emacs will signal a @code{cyclic-function-indirection} error. | ||
| 740 | @end defun | 743 | @end defun |
| 741 | 744 | ||
| 742 | @defun function-alias-p object &optional noerror | 745 | @defun function-alias-p object |
| 743 | Checks whether @var{object} is a function alias. If it is, it returns | 746 | Checks whether @var{object} is a function alias. If it is, it returns |
| 744 | a list of symbols representing the function alias chain, else | 747 | a list of symbols representing the function alias chain, else |
| 745 | @code{nil}. For instance, if @code{a} is an alias for @code{b}, and | 748 | @code{nil}. For instance, if @code{a} is an alias for @code{b}, and |
| @@ -750,9 +753,8 @@ a list of symbols representing the function alias chain, else | |||
| 750 | @result{} (b c) | 753 | @result{} (b c) |
| 751 | @end example | 754 | @end example |
| 752 | 755 | ||
| 753 | If there's a loop in the definitions, an error will be signaled. If | 756 | There is also a second, optional argument that is obsolete and has no |
| 754 | @var{noerror} is non-@code{nil}, the non-looping parts of the chain is | 757 | effect. |
| 755 | returned instead. | ||
| 756 | @end defun | 758 | @end defun |
| 757 | 759 | ||
| 758 | You cannot create a new primitive function with @code{defun} or | 760 | You cannot create a new primitive function with @code{defun} or |
| @@ -1539,6 +1541,9 @@ is not a function, e.g., a keyboard macro (@pxref{Keyboard Macros}): | |||
| 1539 | If you wish to use @code{fset} to make an alternate name for a | 1541 | If you wish to use @code{fset} to make an alternate name for a |
| 1540 | function, consider using @code{defalias} instead. @xref{Definition of | 1542 | function, consider using @code{defalias} instead. @xref{Definition of |
| 1541 | defalias}. | 1543 | defalias}. |
| 1544 | |||
| 1545 | If the resulting function definition chain would be circular, then | ||
| 1546 | Emacs will signal a @code{cyclic-function-indirection} error. | ||
| 1542 | @end defun | 1547 | @end defun |
| 1543 | 1548 | ||
| 1544 | @node Closures | 1549 | @node Closures |
| @@ -327,6 +327,21 @@ This function is like 'user-uid', but is aware of file name handlers, | |||
| 327 | so it will return the remote UID for remote files (or -1 if the | 327 | so it will return the remote UID for remote files (or -1 if the |
| 328 | connection has no associated user). | 328 | connection has no associated user). |
| 329 | 329 | ||
| 330 | +++ | ||
| 331 | ** 'fset' and 'defalias' now signal an error for circular alias chains. | ||
| 332 | Previously, 'fset' and 'defalias' could be made to build circular | ||
| 333 | function indirection chains as in | ||
| 334 | |||
| 335 | (defalias 'able 'baker) | ||
| 336 | (defalias 'baker 'able) | ||
| 337 | |||
| 338 | but trying to call them would often make Emacs hang. Now, an attempt | ||
| 339 | to create such a loop results in an error. | ||
| 340 | |||
| 341 | Since circular alias chains now cannot occur, 'function-alias-p' and | ||
| 342 | 'indirect-function' will never signal an error. Their second | ||
| 343 | 'noerror' arguments have no effect and are therefore obsolete. | ||
| 344 | |||
| 330 | 345 | ||
| 331 | * Changes in Emacs 30.1 on Non-Free Operating Systems | 346 | * Changes in Emacs 30.1 on Non-Free Operating Systems |
| 332 | 347 | ||
diff --git a/lisp/help-fns.el b/lisp/help-fns.el index 8bf8af73d30..1172f068934 100644 --- a/lisp/help-fns.el +++ b/lisp/help-fns.el | |||
| @@ -996,7 +996,7 @@ Returns a list of the form (REAL-FUNCTION DEF ALIASED REAL-DEF)." | |||
| 996 | (symbol-name function))))))) | 996 | (symbol-name function))))))) |
| 997 | (real-def (cond | 997 | (real-def (cond |
| 998 | ((and aliased (not (subrp def))) | 998 | ((and aliased (not (subrp def))) |
| 999 | (car (function-alias-p real-function t))) | 999 | (car (function-alias-p real-function))) |
| 1000 | ((subrp def) (intern (subr-name def))) | 1000 | ((subrp def) (intern (subr-name def))) |
| 1001 | (t def)))) | 1001 | (t def)))) |
| 1002 | 1002 | ||
diff --git a/lisp/subr.el b/lisp/subr.el index 1a4ecc08931..916b6de494b 100644 --- a/lisp/subr.el +++ b/lisp/subr.el | |||
| @@ -7002,27 +7002,17 @@ string will be displayed only if BODY takes longer than TIMEOUT seconds. | |||
| 7002 | (lambda () | 7002 | (lambda () |
| 7003 | ,@body))) | 7003 | ,@body))) |
| 7004 | 7004 | ||
| 7005 | (defun function-alias-p (func &optional noerror) | 7005 | (defun function-alias-p (func &optional _noerror) |
| 7006 | "Return nil if FUNC is not a function alias. | 7006 | "Return nil if FUNC is not a function alias. |
| 7007 | If FUNC is a function alias, return the function alias chain. | 7007 | If FUNC is a function alias, return the function alias chain." |
| 7008 | 7008 | (declare (advertised-calling-convention (func) "30.1") | |
| 7009 | If the function alias chain contains loops, an error will be | 7009 | (side-effect-free error-free)) |
| 7010 | signaled. If NOERROR, the non-loop parts of the chain is returned." | 7010 | (let ((chain nil)) |
| 7011 | (declare (side-effect-free t)) | 7011 | (while (and (symbolp func) |
| 7012 | (let ((chain nil) | 7012 | (setq func (symbol-function func)) |
| 7013 | (orig-func func)) | 7013 | (symbolp func)) |
| 7014 | (nreverse | 7014 | (push func chain)) |
| 7015 | (catch 'loop | 7015 | (nreverse chain))) |
| 7016 | (while (and (symbolp func) | ||
| 7017 | (setq func (symbol-function func)) | ||
| 7018 | (symbolp func)) | ||
| 7019 | (when (or (memq func chain) | ||
| 7020 | (eq func orig-func)) | ||
| 7021 | (if noerror | ||
| 7022 | (throw 'loop chain) | ||
| 7023 | (signal 'cyclic-function-indirection (list orig-func)))) | ||
| 7024 | (push func chain)) | ||
| 7025 | chain)))) | ||
| 7026 | 7016 | ||
| 7027 | (defun readablep (object) | 7017 | (defun readablep (object) |
| 7028 | "Say whether OBJECT has a readable syntax. | 7018 | "Say whether OBJECT has a readable syntax. |
diff --git a/src/data.c b/src/data.c index 1fa8b0358b5..d2f4d40d7bc 100644 --- a/src/data.c +++ b/src/data.c | |||
| @@ -840,7 +840,9 @@ the position will be taken. */) | |||
| 840 | } | 840 | } |
| 841 | 841 | ||
| 842 | DEFUN ("fset", Ffset, Sfset, 2, 2, 0, | 842 | DEFUN ("fset", Ffset, Sfset, 2, 2, 0, |
| 843 | doc: /* Set SYMBOL's function definition to DEFINITION, and return DEFINITION. */) | 843 | doc: /* Set SYMBOL's function definition to DEFINITION, and return DEFINITION. |
| 844 | If the resulting chain of function definitions would contain a loop, | ||
| 845 | signal a `cyclic-function-indirection' error. */) | ||
| 844 | (register Lisp_Object symbol, Lisp_Object definition) | 846 | (register Lisp_Object symbol, Lisp_Object definition) |
| 845 | { | 847 | { |
| 846 | CHECK_SYMBOL (symbol); | 848 | CHECK_SYMBOL (symbol); |
| @@ -852,6 +854,12 @@ DEFUN ("fset", Ffset, Sfset, 2, 2, 0, | |||
| 852 | 854 | ||
| 853 | eassert (valid_lisp_object_p (definition)); | 855 | eassert (valid_lisp_object_p (definition)); |
| 854 | 856 | ||
| 857 | /* Ensure non-circularity. */ | ||
| 858 | for (Lisp_Object s = definition; SYMBOLP (s) && !NILP (s); | ||
| 859 | s = XSYMBOL (s)->u.s.function) | ||
| 860 | if (EQ (s, symbol)) | ||
| 861 | xsignal1 (Qcyclic_function_indirection, symbol); | ||
| 862 | |||
| 855 | #ifdef HAVE_NATIVE_COMP | 863 | #ifdef HAVE_NATIVE_COMP |
| 856 | register Lisp_Object function = XSYMBOL (symbol)->u.s.function; | 864 | register Lisp_Object function = XSYMBOL (symbol)->u.s.function; |
| 857 | 865 | ||
| @@ -1078,7 +1086,7 @@ If CMD is not a command, the return value is nil. | |||
| 1078 | Value, if non-nil, is a list (interactive SPEC). */) | 1086 | Value, if non-nil, is a list (interactive SPEC). */) |
| 1079 | (Lisp_Object cmd) | 1087 | (Lisp_Object cmd) |
| 1080 | { | 1088 | { |
| 1081 | Lisp_Object fun = indirect_function (cmd); /* Check cycles. */ | 1089 | Lisp_Object fun = indirect_function (cmd); |
| 1082 | bool genfun = false; | 1090 | bool genfun = false; |
| 1083 | 1091 | ||
| 1084 | if (NILP (fun)) | 1092 | if (NILP (fun)) |
| @@ -1168,7 +1176,7 @@ If COMMAND is not a command, the return value is nil. | |||
| 1168 | The value, if non-nil, is a list of mode name symbols. */) | 1176 | The value, if non-nil, is a list of mode name symbols. */) |
| 1169 | (Lisp_Object command) | 1177 | (Lisp_Object command) |
| 1170 | { | 1178 | { |
| 1171 | Lisp_Object fun = indirect_function (command); /* Check cycles. */ | 1179 | Lisp_Object fun = indirect_function (command); |
| 1172 | 1180 | ||
| 1173 | if (NILP (fun)) | 1181 | if (NILP (fun)) |
| 1174 | return Qnil; | 1182 | return Qnil; |
| @@ -2482,55 +2490,22 @@ If the current binding is global (the default), the value is nil. */) | |||
| 2482 | 2490 | ||
| 2483 | /* If OBJECT is a symbol, find the end of its function chain and | 2491 | /* If OBJECT is a symbol, find the end of its function chain and |
| 2484 | return the value found there. If OBJECT is not a symbol, just | 2492 | return the value found there. If OBJECT is not a symbol, just |
| 2485 | return it. If there is a cycle in the function chain, signal a | 2493 | return it. */ |
| 2486 | cyclic-function-indirection error. | ||
| 2487 | |||
| 2488 | This is like Findirect_function, except that it doesn't signal an | ||
| 2489 | error if the chain ends up unbound. */ | ||
| 2490 | Lisp_Object | 2494 | Lisp_Object |
| 2491 | indirect_function (register Lisp_Object object) | 2495 | indirect_function (Lisp_Object object) |
| 2492 | { | 2496 | { |
| 2493 | Lisp_Object tortoise, hare; | 2497 | while (SYMBOLP (object) && !NILP (object)) |
| 2494 | 2498 | object = XSYMBOL (object)->u.s.function; | |
| 2495 | hare = tortoise = object; | 2499 | return object; |
| 2496 | |||
| 2497 | for (;;) | ||
| 2498 | { | ||
| 2499 | if (!SYMBOLP (hare) || NILP (hare)) | ||
| 2500 | break; | ||
| 2501 | hare = XSYMBOL (hare)->u.s.function; | ||
| 2502 | if (!SYMBOLP (hare) || NILP (hare)) | ||
| 2503 | break; | ||
| 2504 | hare = XSYMBOL (hare)->u.s.function; | ||
| 2505 | |||
| 2506 | tortoise = XSYMBOL (tortoise)->u.s.function; | ||
| 2507 | |||
| 2508 | if (EQ (hare, tortoise)) | ||
| 2509 | xsignal1 (Qcyclic_function_indirection, object); | ||
| 2510 | } | ||
| 2511 | |||
| 2512 | return hare; | ||
| 2513 | } | 2500 | } |
| 2514 | 2501 | ||
| 2515 | DEFUN ("indirect-function", Findirect_function, Sindirect_function, 1, 2, 0, | 2502 | DEFUN ("indirect-function", Findirect_function, Sindirect_function, 1, 2, 0, |
| 2516 | doc: /* Return the function at the end of OBJECT's function chain. | 2503 | doc: /* Return the function at the end of OBJECT's function chain. |
| 2517 | If OBJECT is not a symbol, just return it. Otherwise, follow all | 2504 | If OBJECT is not a symbol, just return it. Otherwise, follow all |
| 2518 | function indirections to find the final function binding and return it. | 2505 | function indirections to find the final function binding and return it. */) |
| 2519 | Signal a cyclic-function-indirection error if there is a loop in the | 2506 | (Lisp_Object object, Lisp_Object noerror) |
| 2520 | function chain of symbols. */) | ||
| 2521 | (register Lisp_Object object, Lisp_Object noerror) | ||
| 2522 | { | 2507 | { |
| 2523 | Lisp_Object result; | 2508 | return indirect_function (object); |
| 2524 | |||
| 2525 | /* Optimize for no indirection. */ | ||
| 2526 | result = object; | ||
| 2527 | if (SYMBOLP (result) && !NILP (result) | ||
| 2528 | && (result = XSYMBOL (result)->u.s.function, SYMBOLP (result))) | ||
| 2529 | result = indirect_function (result); | ||
| 2530 | if (!NILP (result)) | ||
| 2531 | return result; | ||
| 2532 | |||
| 2533 | return Qnil; | ||
| 2534 | } | 2509 | } |
| 2535 | 2510 | ||
| 2536 | /* Extract and set vector and string elements. */ | 2511 | /* Extract and set vector and string elements. */ |
diff --git a/src/eval.c b/src/eval.c index e377e30c6fb..eb40c953f96 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -2116,7 +2116,7 @@ then strings and vectors are not accepted. */) | |||
| 2116 | 2116 | ||
| 2117 | fun = function; | 2117 | fun = function; |
| 2118 | 2118 | ||
| 2119 | fun = indirect_function (fun); /* Check cycles. */ | 2119 | fun = indirect_function (fun); |
| 2120 | if (NILP (fun)) | 2120 | if (NILP (fun)) |
| 2121 | return Qnil; | 2121 | return Qnil; |
| 2122 | 2122 | ||
diff --git a/test/lisp/help-fns-tests.el b/test/lisp/help-fns-tests.el index 4d715cde1d5..243a45ae6d2 100644 --- a/test/lisp/help-fns-tests.el +++ b/test/lisp/help-fns-tests.el | |||
| @@ -181,10 +181,6 @@ Return first line of the output of (describe-function-1 FUNC)." | |||
| 181 | (ert-deftest help-fns--analyze-function-recursive () | 181 | (ert-deftest help-fns--analyze-function-recursive () |
| 182 | (defalias 'help-fns--a 'help-fns--b) | 182 | (defalias 'help-fns--a 'help-fns--b) |
| 183 | (should (equal (help-fns--analyze-function 'help-fns--a) | 183 | (should (equal (help-fns--analyze-function 'help-fns--a) |
| 184 | '(help-fns--a help-fns--b t help-fns--b))) | ||
| 185 | ;; Make a loop and see that it doesn't infloop. | ||
| 186 | (defalias 'help-fns--b 'help-fns--a) | ||
| 187 | (should (equal (help-fns--analyze-function 'help-fns--a) | ||
| 188 | '(help-fns--a help-fns--b t help-fns--b)))) | 184 | '(help-fns--a help-fns--b t help-fns--b)))) |
| 189 | 185 | ||
| 190 | ;;; help-fns-tests.el ends here | 186 | ;;; help-fns-tests.el ends here |
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 1abd3be4ea1..d5efabc1370 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el | |||
| @@ -1058,10 +1058,12 @@ final or penultimate step during initialization.")) | |||
| 1058 | '(subr-tests--b subr-tests--c))) | 1058 | '(subr-tests--b subr-tests--c))) |
| 1059 | 1059 | ||
| 1060 | (defalias 'subr-tests--d 'subr-tests--e) | 1060 | (defalias 'subr-tests--d 'subr-tests--e) |
| 1061 | (defalias 'subr-tests--e 'subr-tests--d) | 1061 | (should (equal (function-alias-p 'subr-tests--d) |
| 1062 | (should-error (function-alias-p 'subr-tests--d)) | 1062 | '(subr-tests--e))) |
| 1063 | (should (equal (function-alias-p 'subr-tests--d t) | 1063 | |
| 1064 | '(subr-tests--e)))) | 1064 | (fset 'subr-tests--f 'subr-tests--a) |
| 1065 | (should (equal (function-alias-p 'subr-tests--f) | ||
| 1066 | '(subr-tests--a subr-tests--b subr-tests--c)))) | ||
| 1065 | 1067 | ||
| 1066 | (ert-deftest test-readablep () | 1068 | (ert-deftest test-readablep () |
| 1067 | (should (readablep "foo")) | 1069 | (should (readablep "foo")) |
diff --git a/test/src/data-tests.el b/test/src/data-tests.el index 28cee9d2c5b..680fdd57d71 100644 --- a/test/src/data-tests.el +++ b/test/src/data-tests.el | |||
| @@ -772,4 +772,40 @@ comparing the subr with a much slower Lisp implementation." | |||
| 772 | "Can't set variable marked with 'make_symbol_constant'." | 772 | "Can't set variable marked with 'make_symbol_constant'." |
| 773 | (should-error (setq most-positive-fixnum 1) :type 'setting-constant)) | 773 | (should-error (setq most-positive-fixnum 1) :type 'setting-constant)) |
| 774 | 774 | ||
| 775 | (ert-deftest data-tests-fset () | ||
| 776 | (fset 'data-tests--fs-fun (lambda () 'moo)) | ||
| 777 | (declare-function data-tests--fs-fun nil) | ||
| 778 | (should (equal (data-tests--fs-fun) 'moo)) | ||
| 779 | |||
| 780 | (fset 'data-tests--fs-fun1 'data-tests--fs-fun) | ||
| 781 | (declare-function data-tests--fs-fun1 nil) | ||
| 782 | (should (equal (data-tests--fs-fun1) 'moo)) | ||
| 783 | |||
| 784 | (fset 'data-tests--fs-a 'data-tests--fs-b) | ||
| 785 | (fset 'data-tests--fs-b 'data-tests--fs-c) | ||
| 786 | |||
| 787 | (should-error (fset 'data-tests--fs-c 'data-tests--fs-c) | ||
| 788 | :type 'cyclic-function-indirection) | ||
| 789 | (fset 'data-tests--fs-d 'data-tests--fs-a) | ||
| 790 | (should-error (fset 'data-tests--fs-c 'data-tests--fs-d) | ||
| 791 | :type 'cyclic-function-indirection)) | ||
| 792 | |||
| 793 | (ert-deftest data-tests-defalias () | ||
| 794 | (defalias 'data-tests--da-fun (lambda () 'baa)) | ||
| 795 | (declare-function data-tests--da-fun nil) | ||
| 796 | (should (equal (data-tests--da-fun) 'baa)) | ||
| 797 | |||
| 798 | (defalias 'data-tests--da-fun1 'data-tests--da-fun) | ||
| 799 | (declare-function data-tests--da-fun1 nil) | ||
| 800 | (should (equal (data-tests--da-fun1) 'baa)) | ||
| 801 | |||
| 802 | (defalias 'data-tests--da-a 'data-tests--da-b) | ||
| 803 | (defalias 'data-tests--da-b 'data-tests--da-c) | ||
| 804 | |||
| 805 | (should-error (defalias 'data-tests--da-c 'data-tests--da-c) | ||
| 806 | :type 'cyclic-function-indirection) | ||
| 807 | (defalias 'data-tests--da-d 'data-tests--da-a) | ||
| 808 | (should-error (defalias 'data-tests--da-c 'data-tests--da-d) | ||
| 809 | :type 'cyclic-function-indirection)) | ||
| 810 | |||
| 775 | ;;; data-tests.el ends here | 811 | ;;; data-tests.el ends here |