aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNoam Postavsky2017-11-13 12:46:13 -0500
committerNoam Postavsky2017-12-13 17:31:27 -0500
commite7b1111155b3116d0c7b137e0e1d312db0f1ca80 (patch)
tree30240245d971e634ed1ec0f60733fe6dde2e1421
parent4cb8696e4754d815efd5fd5e26f2b6b2567a11fe (diff)
downloademacs-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/NEWS11
-rw-r--r--lisp/emacs-lisp/cl-macs.el38
-rw-r--r--test/lisp/emacs-lisp/cl-macs-tests.el31
3 files changed, 69 insertions, 11 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 64b53d88c83..5324a0944ea 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1462,6 +1462,17 @@ them through 'format' first. Even that is discouraged: for ElDoc
1462support, you should set 'eldoc-documentation-function' instead of 1462support, you should set 'eldoc-documentation-function' instead of
1463calling 'eldoc-message' directly. 1463calling 'eldoc-message' directly.
1464 1464
1465---
1466** Using '&rest' or '&optional' incorrectly is now an error.
1467For example giving '&optional' without a following variable, or
1468passing '&optional' multiple times:
1469
1470 (defun foo (&optional &rest x))
1471 (defun bar (&optional &optional x))
1472
1473Previously, Emacs would just ignore the extra keyword, or give
1474incorrect 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.
505See Bug#29165, and similar `eval-tests--bugs-24912-and-24913' in
506eval-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