diff options
| author | Noam Postavsky | 2017-11-13 12:46:13 -0500 |
|---|---|---|
| committer | Noam Postavsky | 2017-12-13 17:31:27 -0500 |
| commit | e7b1111155b3116d0c7b137e0e1d312db0f1ca80 (patch) | |
| tree | 30240245d971e634ed1ec0f60733fe6dde2e1421 | |
| parent | 4cb8696e4754d815efd5fd5e26f2b6b2567a11fe (diff) | |
| download | emacs-e7b1111155b3116d0c7b137e0e1d312db0f1ca80.tar.gz emacs-e7b1111155b3116d0c7b137e0e1d312db0f1ca80.zip | |
Mention new strictness for &optional, &rest in arglists (Bug#29165)
* etc/NEWS: Explain that '&optional' not followed by a variable is now
an error.
* lisp/emacs-lisp/cl-macs.el (cl--transform-lambda, cl--do-&aux)
(cl--do-arglist): Also reject '&optional', '&rest', or '&aux' not
followed by a variable for consistency.
* test/lisp/emacs-lisp/cl-macs-tests.el (cl-macs-bad-arglist): New
test.
| -rw-r--r-- | etc/NEWS | 11 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-macs.el | 38 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/cl-macs-tests.el | 31 |
3 files changed, 69 insertions, 11 deletions
| @@ -1462,6 +1462,17 @@ them through 'format' first. Even that is discouraged: for ElDoc | |||
| 1462 | support, you should set 'eldoc-documentation-function' instead of | 1462 | support, you should set 'eldoc-documentation-function' instead of |
| 1463 | calling 'eldoc-message' directly. | 1463 | calling 'eldoc-message' directly. |
| 1464 | 1464 | ||
| 1465 | --- | ||
| 1466 | ** Using '&rest' or '&optional' incorrectly is now an error. | ||
| 1467 | For example giving '&optional' without a following variable, or | ||
| 1468 | passing '&optional' multiple times: | ||
| 1469 | |||
| 1470 | (defun foo (&optional &rest x)) | ||
| 1471 | (defun bar (&optional &optional x)) | ||
| 1472 | |||
| 1473 | Previously, Emacs would just ignore the extra keyword, or give | ||
| 1474 | incorrect results in certain cases. | ||
| 1475 | |||
| 1465 | 1476 | ||
| 1466 | * Lisp Changes in Emacs 26.1 | 1477 | * Lisp Changes in Emacs 26.1 |
| 1467 | 1478 | ||
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index 5535100d4ae..6aed060cb50 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el | |||
| @@ -281,8 +281,13 @@ FORM is of the form (ARGS . BODY)." | |||
| 281 | (or (not optional) | 281 | (or (not optional) |
| 282 | ;; Optional args whose default is nil are simple. | 282 | ;; Optional args whose default is nil are simple. |
| 283 | (null (nth 1 (assq (car args) (cdr cl--bind-defs))))) | 283 | (null (nth 1 (assq (car args) (cdr cl--bind-defs))))) |
| 284 | (not (and (eq (car args) '&optional) (setq optional t) | 284 | (not (and (eq (car args) '&optional) |
| 285 | (car cl--bind-defs)))) | 285 | (progn |
| 286 | (when (memq (cadr args) | ||
| 287 | '(nil &rest &body &key &aux)) | ||
| 288 | (error "Variable missing after &optional")) | ||
| 289 | (setq optional t) | ||
| 290 | (car cl--bind-defs))))) | ||
| 286 | (push (pop args) simple-args)) | 291 | (push (pop args) simple-args)) |
| 287 | (when optional | 292 | (when optional |
| 288 | (if args (push '&optional args)) | 293 | (if args (push '&optional args)) |
| @@ -534,14 +539,17 @@ its argument list allows full Common Lisp conventions." | |||
| 534 | arglist)))) | 539 | arglist)))) |
| 535 | 540 | ||
| 536 | (defun cl--do-&aux (args) | 541 | (defun cl--do-&aux (args) |
| 537 | (while (and (eq (car args) '&aux) (pop args)) | 542 | (when (eq (car args) '&aux) |
| 538 | (while (and args (not (memq (car args) cl--lambda-list-keywords))) | 543 | (pop args) |
| 539 | (if (consp (car args)) | 544 | (when (null args) |
| 540 | (if (and cl--bind-enquote (cl-cadar args)) | 545 | (error "Variable missing after &aux"))) |
| 541 | (cl--do-arglist (caar args) | 546 | (while (and args (not (memq (car args) cl--lambda-list-keywords))) |
| 542 | `',(cadr (pop args))) | 547 | (if (consp (car args)) |
| 543 | (cl--do-arglist (caar args) (cadr (pop args)))) | 548 | (if (and cl--bind-enquote (cl-cadar args)) |
| 544 | (cl--do-arglist (pop args) nil)))) | 549 | (cl--do-arglist (caar args) |
| 550 | `',(cadr (pop args))) | ||
| 551 | (cl--do-arglist (caar args) (cadr (pop args)))) | ||
| 552 | (cl--do-arglist (pop args) nil))) | ||
| 545 | (if args (error "Malformed argument list ends with: %S" args))) | 553 | (if args (error "Malformed argument list ends with: %S" args))) |
| 546 | 554 | ||
| 547 | (defun cl--do-arglist (args expr &optional num) ; uses cl--bind-* | 555 | (defun cl--do-arglist (args expr &optional num) ; uses cl--bind-* |
| @@ -558,6 +566,9 @@ its argument list allows full Common Lisp conventions." | |||
| 558 | (keys nil) | 566 | (keys nil) |
| 559 | (laterarg nil) (exactarg nil) minarg) | 567 | (laterarg nil) (exactarg nil) minarg) |
| 560 | (or num (setq num 0)) | 568 | (or num (setq num 0)) |
| 569 | (when (and restarg (or (null (cdr restarg)) | ||
| 570 | (memq (cadr restarg) cl--lambda-list-keywords))) | ||
| 571 | (error "Variable missing after &rest")) | ||
| 561 | (setq restarg (if (listp (cadr restarg)) | 572 | (setq restarg (if (listp (cadr restarg)) |
| 562 | (make-symbol "--cl-rest--") | 573 | (make-symbol "--cl-rest--") |
| 563 | (cadr restarg))) | 574 | (cadr restarg))) |
| @@ -609,7 +620,12 @@ its argument list allows full Common Lisp conventions." | |||
| 609 | `',cl--bind-block) | 620 | `',cl--bind-block) |
| 610 | (+ ,num (length ,restarg))))) | 621 | (+ ,num (length ,restarg))))) |
| 611 | cl--bind-forms))) | 622 | cl--bind-forms))) |
| 612 | (while (and (eq (car args) '&key) (pop args)) | 623 | (while (eq (car args) '&key) |
| 624 | (pop args) | ||
| 625 | (when (or (null args) (memq (car args) cl--lambda-list-keywords)) | ||
| 626 | (error "Missing variable after &key")) | ||
| 627 | (when keys | ||
| 628 | (error "Multiple occurrences of &key")) | ||
| 613 | (while (and args (not (memq (car args) cl--lambda-list-keywords))) | 629 | (while (and args (not (memq (car args) cl--lambda-list-keywords))) |
| 614 | (let ((arg (pop args))) | 630 | (let ((arg (pop args))) |
| 615 | (or (consp arg) (setq arg (list arg))) | 631 | (or (consp arg) (setq arg (list arg))) |
diff --git a/test/lisp/emacs-lisp/cl-macs-tests.el b/test/lisp/emacs-lisp/cl-macs-tests.el index 575f170af6c..bf2e7e12759 100644 --- a/test/lisp/emacs-lisp/cl-macs-tests.el +++ b/test/lisp/emacs-lisp/cl-macs-tests.el | |||
| @@ -497,4 +497,35 @@ collection clause." | |||
| 497 | vconcat (vector (1+ x))) | 497 | vconcat (vector (1+ x))) |
| 498 | [2 3 4 5 6]))) | 498 | [2 3 4 5 6]))) |
| 499 | 499 | ||
| 500 | |||
| 501 | ;;; cl-lib lambda list handling | ||
| 502 | |||
| 503 | (ert-deftest cl-macs-bad-arglist () | ||
| 504 | "Check that `cl-defun' and friends reject weird argument lists. | ||
| 505 | See Bug#29165, and similar `eval-tests--bugs-24912-and-24913' in | ||
| 506 | eval-tests.el." | ||
| 507 | (dolist (args (cl-mapcan | ||
| 508 | ;; For every &rest and &optional variant, check also | ||
| 509 | ;; the same thing with &key and &aux respectively | ||
| 510 | ;; instead. | ||
| 511 | (lambda (arglist) | ||
| 512 | (let ((arglists (list arglist))) | ||
| 513 | (when (memq '&rest arglist) | ||
| 514 | (push (cl-subst '&key '&rest arglist) arglists)) | ||
| 515 | (when (memq '&optional arglist) | ||
| 516 | (push (cl-subst '&aux '&optional arglist) arglists)) | ||
| 517 | arglists)) | ||
| 518 | '((&optional) (&rest) (&optional &rest) (&rest &optional) | ||
| 519 | (&optional &rest _a) (&optional _a &rest) | ||
| 520 | (&rest _a &optional) (&rest &optional _a) | ||
| 521 | (&optional &optional) (&optional &optional _a) | ||
| 522 | (&optional _a &optional _b) | ||
| 523 | (&rest &rest) (&rest &rest _a) | ||
| 524 | (&rest _a &rest _b)))) | ||
| 525 | (ert-info ((prin1-to-string args) :prefix "arglist: ") | ||
| 526 | (should-error (eval `(funcall (cl-function (lambda ,args))) t)) | ||
| 527 | (should-error (cl--transform-lambda (cons args t))) | ||
| 528 | (let ((byte-compile-debug t)) | ||
| 529 | (should-error (eval `(byte-compile (cl-function (lambda ,args))) t)))))) | ||
| 530 | |||
| 500 | ;;; cl-macs-tests.el ends here | 531 | ;;; cl-macs-tests.el ends here |