aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias EngdegÄrd2021-09-23 12:43:41 +0200
committerMattias EngdegÄrd2021-09-25 20:25:01 +0200
commited02b88bbae18caad650d76876940ffb58cab554 (patch)
tree36512b017e92a76a37c63606821274bd35366924
parent80fddff5d64ff915651eb751685b7430de00c536 (diff)
downloademacs-ed02b88bbae18caad650d76876940ffb58cab554.tar.gz
emacs-ed02b88bbae18caad650d76876940ffb58cab554.zip
Renege on anonymous &rest (bug#50268, bug#50720)
Allowing &rest without a variable name following turned out not to be very useful, and it never worked properly. Disallow it. * lisp/emacs-lisp/bytecomp.el (byte-compile-check-lambda-list): * src/eval.c (funcall_lambda): Signal error for &rest without variable name. * doc/lispref/functions.texi (Argument List): Adjust manual. * etc/NEWS (file): Announce. * test/src/eval-tests.el (eval-tests--bugs-24912-and-24913): Extend test, also checking with and without lexical binding. (eval-tests-accept-empty-optional-rest): Reduce to... (eval-tests-accept-empty-optional): ...this, again checking with and without lexical binding.
-rw-r--r--doc/lispref/functions.texi2
-rw-r--r--etc/NEWS7
-rw-r--r--lisp/emacs-lisp/bytecomp.el2
-rw-r--r--src/eval.c9
-rw-r--r--test/src/eval-tests.el57
5 files changed, 49 insertions, 28 deletions
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 77d1465c876..c856557c3cb 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -378,7 +378,7 @@ keyword @code{&rest} before one final argument.
378@group 378@group
379(@var{required-vars}@dots{} 379(@var{required-vars}@dots{}
380 @r{[}&optional @r{[}@var{optional-vars}@dots{}@r{]}@r{]} 380 @r{[}&optional @r{[}@var{optional-vars}@dots{}@r{]}@r{]}
381 @r{[}&rest @r{[}@var{rest-var}@r{]}@r{]}) 381 @r{[}&rest @var{rest-var}@r{]})
382@end group 382@end group
383@end example 383@end example
384 384
diff --git a/etc/NEWS b/etc/NEWS
index f211a98678c..61780a3a19e 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3273,6 +3273,13 @@ local variable would not be heeded. This has now changed, and a file
3273with a 'lexical-binding' cookie is always heeded. To revert to the 3273with a 'lexical-binding' cookie is always heeded. To revert to the
3274old behavior, set 'permanently-enabled-local-variables' to nil. 3274old behavior, set 'permanently-enabled-local-variables' to nil.
3275 3275
3276+++
3277** '&rest' in argument lists must always be followed by a variable name.
3278Omitting the variable name after '&rest' was previously tolerated in
3279some cases but not consistently so; it could lead to crashes or
3280outright wrong results. Since the utility was marginal at best, it is
3281now an error to omit the variable.
3282
3276--- 3283---
3277** 'kill-all-local-variables' has changed how it handles non-symbol hooks. 3284** 'kill-all-local-variables' has changed how it handles non-symbol hooks.
3278The function is documented to eliminate all buffer-local bindings 3285The function is documented to eliminate all buffer-local bindings
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index be74195778b..d7da7a2149a 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -2930,6 +2930,8 @@ If FORM is a lambda or a macro, byte-compile it as a function."
2930 (macroexp--const-symbol-p arg t)) 2930 (macroexp--const-symbol-p arg t))
2931 (error "Invalid lambda variable %s" arg)) 2931 (error "Invalid lambda variable %s" arg))
2932 ((eq arg '&rest) 2932 ((eq arg '&rest)
2933 (unless (cdr list)
2934 (error "&rest without variable name"))
2933 (when (cddr list) 2935 (when (cddr list)
2934 (error "Garbage following &rest VAR in lambda-list")) 2936 (error "Garbage following &rest VAR in lambda-list"))
2935 (when (memq (cadr list) '(&optional &rest)) 2937 (when (memq (cadr list) '(&optional &rest))
diff --git a/src/eval.c b/src/eval.c
index 2bb7cfe6002..66d34808f82 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -3245,6 +3245,7 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
3245 emacs_abort (); 3245 emacs_abort ();
3246 3246
3247 i = optional = rest = 0; 3247 i = optional = rest = 0;
3248 bool previous_rest = false;
3248 for (; CONSP (syms_left); syms_left = XCDR (syms_left)) 3249 for (; CONSP (syms_left); syms_left = XCDR (syms_left))
3249 { 3250 {
3250 maybe_quit (); 3251 maybe_quit ();
@@ -3255,13 +3256,14 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
3255 3256
3256 if (EQ (next, Qand_rest)) 3257 if (EQ (next, Qand_rest))
3257 { 3258 {
3258 if (rest) 3259 if (rest || previous_rest)
3259 xsignal1 (Qinvalid_function, fun); 3260 xsignal1 (Qinvalid_function, fun);
3260 rest = 1; 3261 rest = 1;
3262 previous_rest = true;
3261 } 3263 }
3262 else if (EQ (next, Qand_optional)) 3264 else if (EQ (next, Qand_optional))
3263 { 3265 {
3264 if (optional || rest) 3266 if (optional || rest || previous_rest)
3265 xsignal1 (Qinvalid_function, fun); 3267 xsignal1 (Qinvalid_function, fun);
3266 optional = 1; 3268 optional = 1;
3267 } 3269 }
@@ -3287,10 +3289,11 @@ funcall_lambda (Lisp_Object fun, ptrdiff_t nargs,
3287 else 3289 else
3288 /* Dynamically bind NEXT. */ 3290 /* Dynamically bind NEXT. */
3289 specbind (next, arg); 3291 specbind (next, arg);
3292 previous_rest = false;
3290 } 3293 }
3291 } 3294 }
3292 3295
3293 if (!NILP (syms_left)) 3296 if (!NILP (syms_left) || previous_rest)
3294 xsignal1 (Qinvalid_function, fun); 3297 xsignal1 (Qinvalid_function, fun);
3295 else if (i < nargs) 3298 else if (i < nargs)
3296 xsignal2 (Qwrong_number_of_arguments, fun, make_fixnum (nargs)); 3299 xsignal2 (Qwrong_number_of_arguments, fun, make_fixnum (nargs));
diff --git a/test/src/eval-tests.el b/test/src/eval-tests.el
index b2b7dfefda5..3c3e7033419 100644
--- a/test/src/eval-tests.el
+++ b/test/src/eval-tests.el
@@ -39,31 +39,40 @@
39(ert-deftest eval-tests--bugs-24912-and-24913 () 39(ert-deftest eval-tests--bugs-24912-and-24913 ()
40 "Check that Emacs doesn't accept weird argument lists. 40 "Check that Emacs doesn't accept weird argument lists.
41Bug#24912 and Bug#24913." 41Bug#24912 and Bug#24913."
42 (dolist (args '((&rest &optional) 42 (dolist (lb '(t false))
43 (&rest a &optional) (&rest &optional a) 43 (ert-info ((prin1-to-string lb) :prefix "lexical-binding: ")
44 (&optional &optional) (&optional &optional a) 44 (let ((lexical-binding lb))
45 (&optional a &optional b) 45 (dolist (args '((&rest &optional)
46 (&rest &rest) (&rest &rest a) 46 (&rest a &optional) (&rest &optional a)
47 (&rest a &rest b))) 47 (&optional &optional) (&optional &optional a)
48 (should-error (eval `(funcall (lambda ,args)) t) :type 'invalid-function) 48 (&optional a &optional b)
49 (should-error (byte-compile-check-lambda-list args)) 49 (&rest &rest) (&rest &rest a)
50 (let ((byte-compile-debug t)) 50 (&rest a &rest b)
51 (ert-info ((format "bytecomp: args = %S" args)) 51 (&rest) (&optional &rest)
52 (should-error (eval `(byte-compile (lambda ,args)) t)))))) 52 ))
53 53 (ert-info ((prin1-to-string args) :prefix "args: ")
54(ert-deftest eval-tests-accept-empty-optional-rest () 54 (should-error
55 "Check that Emacs accepts empty &optional and &rest arglists. 55 (eval `(funcall (lambda ,args)) lb) :type 'invalid-function)
56 (should-error (byte-compile-check-lambda-list args))
57 (let ((byte-compile-debug t))
58 (should-error (eval `(byte-compile (lambda ,args)) lb)))))))))
59
60(ert-deftest eval-tests-accept-empty-optional ()
61 "Check that Emacs accepts empty &optional arglists.
56Bug#24912." 62Bug#24912."
57 (dolist (args '((&optional) (&rest) (&optional &rest) 63 (dolist (lb '(t false))
58 (&optional &rest a) (&optional a &rest))) 64 (ert-info ((prin1-to-string lb) :prefix "lexical-binding: ")
59 (let ((fun `(lambda ,args 'ok))) 65 (let ((lexical-binding lb))
60 (ert-info ("eval") 66 (dolist (args '((&optional) (&optional &rest a)))
61 (should (eq (funcall (eval fun t)) 'ok))) 67 (ert-info ((prin1-to-string args) :prefix "args: ")
62 (ert-info ("byte comp check") 68 (let ((fun `(lambda ,args 'ok)))
63 (byte-compile-check-lambda-list args)) 69 (ert-info ("eval")
64 (ert-info ("bytecomp") 70 (should (eq (funcall (eval fun lb)) 'ok)))
65 (let ((byte-compile-debug t)) 71 (ert-info ("byte comp check")
66 (should (eq (funcall (byte-compile fun)) 'ok))))))) 72 (byte-compile-check-lambda-list args))
73 (ert-info ("bytecomp")
74 (let ((byte-compile-debug t))
75 (should (eq (funcall (byte-compile fun)) 'ok)))))))))))
67 76
68 77
69(dolist (form '(let let*)) 78(dolist (form '(let let*))