diff options
| author | Mattias EngdegÄrd | 2021-09-23 12:43:41 +0200 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2021-09-25 20:25:01 +0200 |
| commit | ed02b88bbae18caad650d76876940ffb58cab554 (patch) | |
| tree | 36512b017e92a76a37c63606821274bd35366924 | |
| parent | 80fddff5d64ff915651eb751685b7430de00c536 (diff) | |
| download | emacs-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.texi | 2 | ||||
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | lisp/emacs-lisp/bytecomp.el | 2 | ||||
| -rw-r--r-- | src/eval.c | 9 | ||||
| -rw-r--r-- | test/src/eval-tests.el | 57 |
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 | ||
| @@ -3273,6 +3273,13 @@ local variable would not be heeded. This has now changed, and a file | |||
| 3273 | with a 'lexical-binding' cookie is always heeded. To revert to the | 3273 | with a 'lexical-binding' cookie is always heeded. To revert to the |
| 3274 | old behavior, set 'permanently-enabled-local-variables' to nil. | 3274 | old behavior, set 'permanently-enabled-local-variables' to nil. |
| 3275 | 3275 | ||
| 3276 | +++ | ||
| 3277 | ** '&rest' in argument lists must always be followed by a variable name. | ||
| 3278 | Omitting the variable name after '&rest' was previously tolerated in | ||
| 3279 | some cases but not consistently so; it could lead to crashes or | ||
| 3280 | outright wrong results. Since the utility was marginal at best, it is | ||
| 3281 | now 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. |
| 3278 | The function is documented to eliminate all buffer-local bindings | 3285 | The 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. |
| 41 | Bug#24912 and Bug#24913." | 41 | Bug#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. | ||
| 56 | Bug#24912." | 62 | Bug#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*)) |